C语言经典算法-5

文章目录

  • 其他经典例题跳转链接
    • 26.约瑟夫问题(Josephus Problem)
    • 27.排列组合
    • 28.格雷码(Gray Code)
    • 29.产生可能的集合
    • 30.m元素集合的n个元素子集

其他经典例题跳转链接

C语言经典算法-1
1.汉若塔 2. 费式数列 3. 巴斯卡三角形 4. 三色棋 5. 老鼠走迷官(一)6. 老鼠走迷官(二)7. 骑士走棋盘8. 八皇后9. 八枚银币10. 生命游戏

C语言经典算法-2
字串核对、双色、三色河内塔、背包问题(Knapsack Problem)、蒙地卡罗法求 PI、Eratosthenes筛选求质数

C语言经典算法-3
超长整数运算(大数运算)、长 PI、最大公因数、最小公倍数、因式分解、完美数、阿姆斯壮数

C语言经典算法-4
最大访客数、中序式转后序式(前序式)、后序式的运算、洗扑克牌(乱数排列)、Craps赌博游戏

C语言经典算法-5
约瑟夫问题(Josephus Problem)、排列组合、格雷码(Gray Code)、产生可能的集合、m元素集合的n个元素子集

C语言经典算法-6
数字拆解、得分排行,选择、插入、气泡排序、Shell 排序法 - 改良的插入排序、Shaker 排序法 - 改良的气泡排序

C语言经典算法-7
排序法 - 改良的选择排序、快速排序法(一)、快速排序法(二)、快速排序法(三)、合并排序法

C语言经典算法-8
基数排序法、循序搜寻法(使用卫兵)、二分搜寻法(搜寻原则的代表)、插补搜寻法、费氏搜寻法

C语言经典算法-9
稀疏矩阵、多维矩阵转一维矩阵、上三角、下三角、对称矩阵、奇数魔方阵、4N 魔方阵、2(2N+1) 魔方阵

26.约瑟夫问题(Josephus Problem)

说明据说着名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人 开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
解法约瑟夫问题可用代数分析来求解,将这个问题扩大好了,假设现在您与m个朋友不幸参与了这个游戏,您要如何保护您与您的朋友?只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆圈内圈是排列顺序,而外圈是自杀顺序,如下图所示:

在这里插入图片描述

使用程式来求解的话,只要将阵列当作环状来处理就可以了,在阵列中由计数1开始,每找到三个无资料区就填入一个计数,直而计数达41为止,然后将阵列由索引1开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列,41个人而报数3的约琴夫排列如下所示:

14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23

由上可知,最后一个自杀的是在第31个位置,而倒数第二个自杀的要排在第16个位置,之前的人都死光了,所以他们也就不知道约琴夫与他的朋友并没有遵守游戏规则了。

#include <stdio.h> 
#include <stdlib.h> 
#define N 41 
#define M 3 

int main(void) { 
    int man[N] = {0}; 
    int count = 1; 
    int i = 0, pos = -1; 
    int alive = 0; 

    while(count <= N) { 
        do { 
            pos = (pos+1) % N;  // 环状处理 
            if(man[pos] == 0) 
                i++; 

            if(i == M) {  // 报数为3了 
                i = 0; 
                break; 
            } 
        } while(1); 

        man[pos] = count; 
        count++; 
    } 

    printf("\n约琴夫排列:"); 
    for(i = 0; i < N; i++) 
        printf("%d ", man[i]); 
    printf("\n\n您想要救多少人?"); 
    scanf("%d", &alive); 

    printf("\nL表示这%d人要放的位置:\n", alive); 
    for(i = 0; i < N; i++) { 
        if(man[i] > alive) 	printf("D"); 
        else 	printf("L"); 
        if((i+1) % 5 == 0) 	printf("  "); 
    } 
    printf("\n"); 
    return 0; } 

27.排列组合

说明将一组数字、字母或符号进行排列,以得到不同的组合顺序,例如1 2 3这三个数的排列组合有:1 2 3、1 3 2、2 1 3、2 3 1、3 1 2、3 2 1。
解法可以使用递回将问题切割为较小的单元进行排列组合,例如1 2 3 4的排列可以分为1 [2 3 4]、2 [1 3 4]、3 [1 2 4]、4 [1 2 3]进行排列,这边利用旋转法,先将旋转间隔设为0,将最右边的数字旋转至最左边,并逐步增加旋转的间隔,例如:

1 2 3 4 -> 旋转1 -> 继续将右边2 3 4进行递回处理
2 1 3 4 -> 旋转1 2 变为 2 1-> 继续将右边1 3 4进行递回处理
3 1 2 4 -> 旋转1 2 3变为 3 1 2 -> 继续将右边1 2 4进行递回处理
4 1 2 3 -> 旋转1 2 3 4变为4 1 2 3 -> 继续将右边1 2 3进行递回处理

#include <stdio.h> 
#include <stdlib.h> 
#define N 4 

void perm(int*, int); 

int main(void) { 
    int num[N+1], i; 
    for(i = 1; i <= N; i++) 
        num[i] = i; 
    perm(num, 1); 
    return 0; 
} 

void perm(int* num, int i) { 
    int j, k, tmp; 

    if(i < N) { 
        for(j = i; j <= N; j++) { 
            tmp = num[j]; 
            // 旋转该区段最右边数字至最左边 
            for(k = j; k > i; k--) 
                num[k] = num[k-1]; 
            num[i] = tmp; 
            perm(num, i+1); 
            // 还原 
            for(k = i; k < j; k++) 
                num[k] = num[k+1]; 
            num[j] = tmp; 
        } 
    } 
    else {  // 显示此次排列 
        for(j = 1; j <= N; j++) 
            printf("%d ", num[j]); 
        printf("\n"); 
    } 
}  

28.格雷码(Gray Code)

说明
Gray Code是一个数列集合,每个数使用二进位来表示,假设使用n位元来表示每个数好了,任两个数之间只有一个位元值不同,例如以下为3位元的Gray Code:

000 001 011 010 110 111 101 100

由定义可以知道,Gray Code的顺序并不是唯一的,例如将上面的数列反过来写,也是一组Gray Code:

100 101 111 110 010 011 001 000

Gray Code是由贝尔实验室的Frank Gray在1940年代提出的,用来在使用PCM(Pusle Code Modulation)方法传送讯号时避免出错,并于1953年三月十七日取得美国专利。
解法
由于Gray Code相邻两数之间只改变一个位元,所以可观 察Gray Code从1变0或从0变1时的位置,假设有4位元的Gray Code如下:

0000 0001 0011 0010 0110 0111 0101 0100
1100 1101 1111 1110 1010 1011 1001 1000

观察奇数项的变化时,我们发现无论它是第几个Gray Code,永远只改变最右边的位元,如果是1就改为0,如果是0就改为1。

观察偶数项的变化时,我们发现所改变的位元,是由右边算来第一个1的左边位元。

以上两个变化规则是固定的,无论位元数为何;所以只要判断位元的位置是奇数还是偶数,就可以决定要改变哪一个位元的值,为了程式撰写方便,将阵列索引 0当作最右边的值,而在列印结果时,是由索引数字大的开始反向列印。

将2位元的Gray Code当作平面座标来看,可以构成一个四边形,您可以发现从任一顶点出发,绕四边形周长绕一圈,所经过的顶点座标就是一组Gray Code,所以您可以得到四组Gray Code。

同样的将3位元的Gray Code当作平面座标来看的话,可以构成一个正立方体,如果您可以从任一顶点出发,将所有的边长走过,并不重复经过顶点的话,所经过的顶点座标顺序之组合也就是一组Gray Code。

#include <stdio.h> 
#include <stdlib.h> 

#define MAXBIT 20 
#define TRUE 1 
#define CHANGE_BIT(x) x = ((x) == '0' ? '1' : '0') 
#define NEXT(x) x = (1 - (x)) 

int main(void) { 
    char digit[MAXBIT]; 
    int i, bits, odd; 

    printf("输入位元数:"); 
    scanf("%d", &bits); 

    for(i = 0; i < bits; i++) { 
        digit[i] = '0'; 
        printf("0"); 
    } 

    printf("\n"); 

    odd = TRUE; 

    while(1) { 
        if(odd) 
            CHANGE_BIT(digit[0]); 
        else { 
            // 计算第一个1的位置 
            for(i = 0; i < bits && digit[i] == '0'; i++) ; 
            if(i == bits - 1) // 最后一个Gray Code 
                 break; 
            CHANGE_BIT(digit[i+1]); 
        } 
        for(i = bits - 1; i >= 0; i--) 
            printf("%c", digit[i]); 

        printf("\n"); 
        NEXT(odd); 
    } 
    return 0; 
} 

29.产生可能的集合

说明
给定一组数字或符号,产生所有可能的集合(包括空集合),例如给定1 2 3,则可能的集合为:{}、{1}、{1,2}、{1,2,3}、{1,3}、{2}、{2,3}、{3}。
解法
如果不考虑字典顺序,则有个简单的方法可以产生所有的集合,思考二进位数字加法,并注意1出现的位置,如果每个位置都对应一个数字,则由1所对应的数字所产生的就是一个集合,例如:

InputSet
000{}
001{3}
010{2}
011{2,3}
100{1}
101{1,3}
110{1,2}
111{1,2,3}

了解这个方法之后,剩下的就是如何产生二进位数?有许多方法可以使用,您可以使用unsigned型别加上&位元运算来产生,这边则是使用阵列搜 寻,首先阵列内容全为0,找第一个1,在还没找到之前将走访过的内容变为0,而第一个找到的0则变为 1,如此重复直到所有的阵列元素都变为1为止,例如:
000 => 100 => 010 => 110 => 001 => 101 => 011 => 111

如果要产生字典顺序,例如若有4个元素,则:
{} => {1} => {1,2} => {1,2,3} => {1,2,3,4} =>
{1,2,4} =>
{1,3} => {1,3,4} =>
{1,4} =>
{2} => {2,3} => {2,3,4} =>
{2,4} =>
{3} => {3,4} =>
{4}

简单的说,如果有n个元素要产生可能的集合,当依序产生集合时,如果最后一个元素是n,而倒数第二个元素是m的话,例如:
{a b c d e n}

则下一个集合就是{a b c d e+1},再依序加入后续的元素。

例如有四个元素,而当产生{1 2 3 4}集合时,则下一个集合就是{1 2 3+1},也就是{1 2 4},由于最后一个元素还是4,所以下一个集合就是{1 2+1},也就是{1 3},接下来再加入后续元素4,也就是{1 3 4},由于又遇到元素4,所以下一个集合是{1 3+1},也就是{1 4}。
实作

C(无字典顺序) 
#include <stdio.h> 
#include <stdlib.h> 

#define MAXSIZE 20 

int main(void) { 
    char digit[MAXSIZE]; 
    int i, j; 
    int n; 

    printf("输入集合个数:"); 
    scanf("%d", &n); 

    for(i = 0; i < n; i++) 
        digit[i] = '0'; 

    printf("\n{}"); // 空集合 

    while(1) { 
        // 找第一个0,并将找到前所经过的元素变为0 
        for(i = 0; i < n && digit[i] == '1'; digit[i] = '0', i++); 

        if(i == n)  // 找不到0 
            break; 
        else          // 将第一个找到的0变为1 
            digit[i] = '1'; 

        // 找第一个1,并记录对应位置 
        for(i = 0; i < n && digit[i] == '0'; i++); 

        printf("\n{%d", i+1); 
    
        for(j = i + 1; j < n; j++) 
            if(digit[j] == '1') 
                printf(",%d", j + 1); 

        printf("}"); 
    } 
    
    printf("\n"); 

    return 0; 
} 
C(字典顺序) 
#include <stdio.h> 
#include <stdlib.h> 

#define MAXSIZE 20 

int main(void) { 
    int set[MAXSIZE]; 
    int i, n, position = 0; 

    printf("输入集合个数:"); 
    scanf("%d", &n); 
    printf("\n{}"); 
    set[position] = 1; 

    while(1) { 
        printf("\n{%d", set[0]);  // 印第一个数 
        for(i = 1; i <= position; i++) 
            printf(",%d", set[i]); 
        printf("}"); 

        if(set[position] < n) {  // 递增集合个数 
            set[position+1] = set[position] + 1; 
            position++; 
        } 
        else if(position != 0) {  // 如果不是第一个位置 
            position--;       // 倒退 
            set[position]++;  // 下一个集合尾数 
        } 
        else  // 已倒退至第一个位置 
            break; 
    } 

    printf("\n"); 

    return 0; 
} 

30.m元素集合的n个元素子集

说明
假设有个集合拥有m个元素,任意的从集合中取出n个元素,则这n个元素所形成的可能子集有那些?
解法
假设有5个元素的集点,取出3个元素的可能子集如下:
{1 2 3}、{1 2 4 }、{1 2 5}、{1 3 4}、{1 3 5}、{1 4 5}、{2 3 4}、{2 3 5}、{2 4 5}、{3 4 5}

这些子集已经使用字典顺序排列,如此才可以观察出一些规则:
如果最右一个元素小于m,则如同码表一样的不断加1
如果右边一位已至最大值,则加1的位置往左移
每次加1的位置往左移后,必须重新调整右边的元素为递减顺序

所以关键点就在于哪一个位置必须进行加1的动作,到底是最右一个位置要加1?还是其它的位置?

在实际撰写程式时,可以使用一个变数positon来记录加1的位置,position的初值设定为n-1,因为我们要使用阵列,而最右边的索引值为最大 的n-1,在position位置的值若小于m就不断加1,如果大于m了,position就减1,也就是往左移一个位置;由于位置左移后,右边的元素会 经过调整,所以我们必须检查最右边的元素是否小于m,如果是,则position调整回n-1,如果不是,则positon维持不变。
实作

#include <stdio.h> 
#include <stdlib.h> 

#define MAX 20 

int main(void) { 
    int set[MAX]; 
    int m, n, position; 
    int i; 

    printf("输入集合个数 m:"); 
    scanf("%d", &m); 
    printf("输入取出元素 n:"); 
    scanf("%d", &n); 

    for(i = 0; i < n; i++) 
        set[i] = i + 1; 

    // 显示第一个集合 
    for(i = 0; i < n; i++) 
        printf("%d ", set[i]); 
    putchar('\n'); 
    
    position = n - 1; 

    while(1) { 
        if(set[n-1] == m) 
            position--; 
        else 
            position = n - 1; 

        set[position]++; 

        // 调整右边元素 
        for(i = position + 1; i < n; i++) 
            set[i] = set[i-1] + 1; 

        for(i = 0; i < n; i++) 
            printf("%d ", set[i]); 
        putchar('\n'); 

        if(set[0] >= m - n + 1) 
            break; 
    } 

    return 0; 
} 

系列好文,点击链接即可跳转
上一篇:
C语言经典算法-4
最大访客数、中序式转后序式(前序式)、后序式的运算、洗扑克牌(乱数排列)、Craps赌博游戏
下一篇:
C语言经典算法-6
数字拆解、得分排行,选择、插入、气泡排序、Shell 排序法 - 改良的插入排序、Shaker 排序法 - 改良的气泡排序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/474847.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Spring IOC/DI】bean 的 5 种注册 与 5 种注入

什么是 bean 一个 bean 就是一个实例化对象 User user new User() 上面这行代码中的 user&#xff0c; 就是 User 类的实例化对象&#xff0c;即一个 bean&#xff08;User Bean&#xff09; 什么是 IOC Inversion of Control 控制反转&#xff08;反转对 bean 的控制&#…

ElasticSearch之Ingest Pipeline和Painless Script

写在前面 如果是我们需要在写入文档或者是返回文档时&#xff0c;进行修改字段值&#xff0c;或者增加字段等操作时&#xff0c;就可以考虑使用ingest pipeline和painless script。如下的需求&#xff1a; 1:ingest pipeline 在es 5中引入了一种新的节点类型ingest node&am…

安科瑞智能断路器产品介绍【可监可控 远程操控 短路保护】

开发背景 过去几年智慧用电的产品应用中&#xff0c;大多数只安装于进线测。主要存在以下几个问题&#xff1a;难定位&#xff0c;不知道具体哪个回路出线问题&#xff0c;排查困难&#xff1b;出线过载或线缆温度过高无法知晓&#xff1b;即使是出线回路安装了的场景&#xf…

个人开发App成功上架手机应用市场的关键步骤

目录 1. 苹果审核和APP备案 2. APP上架操作步骤 3. 审核和发布 4. 上线工作 总结 参考资料 在当前移动应用市场竞争激烈的背景下&#xff0c;个人开发App如何成功上架成为开发者们必须面对的重要任务。本文将重点介绍自建App上架至手机应用市场的流程&#xff0c;包括苹果…

2024你必须知道的外贸形势!

2024年外贸形势下的新机会在哪里&#xff1f;今天Erica给大家总结了几个主要市场的形式。 喜欢的话点点关注吧~ 欧美市场2024年应谨慎开发 海关总署11月7日发布的数据显示&#xff0c;前10个月&#xff0c;今年中国对欧洲出口呈下降趋势&#xff0c;中国与欧盟贸易总值为4.5…

前端项目,个人笔记(六)【无限滚动 + 拦截器】

目录 1、无限滚动 2、使用pinia进行用户数据持久化 3、完善个人笔记三中的拦截器 请求拦截器&#xff1a; 响应拦截器&#xff1a; 1、无限滚动 使用elementplus中提供的&#xff1a; 代码&#xff1a; <div class"body" v-infinite-scroll"load"…

【复现】某指挥调度管理平台 SQL注入漏洞_66

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 该平台提供强大的指挥调度功能&#xff0c;可以实时监控和管理通信网络设备、维护人员和工作任务等。用户可以通过该平台发送指令…

积鼎CFD发动机燃烧仿真,实现航空航天发动机内部燃烧过程的流体仿真

航空航天发动机中的燃烧现象是一种复杂的物理化学过程&#xff0c;包括流动、雾化、相变、传热传质、点火熄火、化学反应、污染物排放、热声振荡和冷却等多个过程&#xff0c;加上燃烧的非定常性和高湍流度&#xff0c;使得准确模拟燃烧过程变得异常困难。在传统CFD模拟需要考虑…

Docker在Mac上轻松部署RabbitMQ:从拉取镜像到创建运行带管理界面的容器全攻略

1、去官网下载docker 安装&#xff1a;把图标拉到应用程序即可 https://docs.docker.com/desktop/install/mac-install/ 2、拉取rabbitmq镜像 docker pull rabbitmq:3.8-management 3、创建并启动容器&#xff0c;同时设置环境变量以创建用户和密码 docker run -d --name m…

python中医学习服务管理系统flask-django-php-nodejs

随着世界经济信息化、全球化的到来和互联网的飞速发展&#xff0c;推动了各行业的改革。若想达到安全&#xff0c;快捷的目的&#xff0c;就需要拥有信息化的组织和管理模式&#xff0c;建立一套合理、动态的、交互友好的、高效的中医学习服务管理系统。当前的信息管理存在工作…

YOLOV5 部署:cuda和cuDNN安装

1、前言 TensorRT 的安装需要配合cuda的使用,所以这里需要安装cuda和cudnn用于加速推理 TensorRT 就是神经网络专门用来加速的框架 之前训练yolov5项目的时候,我们只是配置了torch的GPU环境,没有专门安装cuda和cudnn,因为简单的训练、推理没必要cuda加速。 torch的GPU配置…

MINT: Detecting Fraudulent Behaviors from Time-series Relational Data论文阅读笔记

2. 问题定义 时间序列关系数据&#xff08;Time Series Relation Data&#xff09; 这个数据是存放在关系型数据库中&#xff0c;每一条记录都是泰永时间搓的行为。 更具体地&#xff0c;每条记录表示为 x ( v , t , x 1 , x 2 , … , x m − 2 ) x (v,t,x_1,x_2,\dots,x…

【JS】浅谈Promise

Promise 前言一、Promise是什么&#xff1f;二、为什么用Promise&#xff1f;2.1解决回调地狱2.2 集中错误处理2.3代码解耦和复用 三、做什么&#xff1f;四、原型方法和实例方法&#xff1f;五、应用场景&#xff1f; 前言 promise是es6的新规范&#xff0c;它是一种异步解决…

粗糙度对应表,觉得挺实用

粗糙度新老标准经常会遇到&#xff0c;分享给大家

大数据分析师特训营介绍

大数据分析师是做什么的&#xff1f; 数据分析师是在不同行业中&#xff0c;专门从事行业数据搜集、整理、分析&#xff0c;并依据数据做出行业研究、评估和预测等工作的。与传统的数据分析师相比&#xff0c;大数据分析师要学会打破信息孤岛利用各种数据源&#xff0c;在海量…

ByteTrack多目标跟踪——YOLOX详解

文章目录 1 before train1.1 dataset1.2 model 2 train2.1 Backbone2.2 PAFPN2.3 Head2.3.1 Decoupled Head2.3.2 anchor-free2.3.3 标签分配① 初步筛选② simOTA 2.3.4 Loss计算 项目地址&#xff1a; ByteTrack ByteTrack使用的检测器是YOLOX&#xff0c;是一个目前非常流行…

Ceres求解非线性优化问题步骤与示例

【版权声明】 本文为博主原创文章&#xff0c;未经博主允许严禁转载&#xff0c;我们会定期进行侵权检索。 在计算机视觉和机器人领域&#xff0c;经常需要解决非线性优化问题来估计相机姿态或运动模型。Ceres Solver是一个开源的C库&#xff0c;专门用于解决最小二乘问题&am…

Linux系统如何使用tcpdump实时监控网络速度:方法与技巧解析

在网络管理和故障排查中&#xff0c;了解网络速度是一个重要的环节。而tcpdump&#xff0c;作为一个强大的网络数据包分析工具&#xff0c;不仅可以用于分析数据包的内容&#xff0c;还能用于实时监控网络速度。本文将介绍Linux系统如何使用tcpdump来实时监控网络速度。 首先&…

智能型程控直流电子负载特点和特性

智能型程控直流电子负载是高精度、高稳定性的电源测试设备&#xff0c;主要用于对电源、电池、充电器等直流电源设备的输出性能进行测试。它具有以下特点和特性&#xff1a; 智能型程控直流电子负载采用先进的控制算法和高精度的ADC&#xff0c;能够实现对电流、电压、功率等参…

【EOJ】2985.圆和正方形

单点时限: 2.0 sec 内存限制: 256 MB 小王首先在平面上画一个边长为 K 的正方形 S1&#xff0c;然后又画一个 S1 的内切圆 C1&#xff0c;这算做一次操作。然后接着画 C1 的一个内切正方形 S2&#xff0c;和 S2 的一个内切圆 C2&#xff0c;这算第二次操作。他一直进行了 K 次…