数组元素全排列、组合 C语言代码

您所在的位置:网站首页 c语言给一组数排序 数组元素全排列、组合 C语言代码

数组元素全排列、组合 C语言代码

2024-07-09 17:52:22| 来源: 网络整理| 查看: 265

permutation 排列 combination 组合

一、题目来源

Linux C编程一站式学习课后练习题: https://akaedu.github.io/book/ch08s03.html

定义一个数组,编程打印它的全排列。比如定义:

#define N 3 int a[N] = { 1, 2, 3 };

则运行结果是:

$ ./a.out 1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 3 1 2 1 2 3

程序的主要思路是:

把第1个数换到最前面来(本来就在最前面),准备打印1xx,再对后两个数2和3做全排列。

把第2个数换到最前面来,准备打印2xx,再对后两个数1和3做全排列。

把第3个数换到最前面来,准备打印3xx,再对后两个数1和2做全排列。

可见这是一个递归的过程,把对整个序列做全排列的问题归结为对它的子序列做全排列的问题,注意我没有描述Base Case怎么处理,你需要自己想。你的程序要具有通用性,如果改变了N和数组a的定义(比如改成4个数的数组),其它代码不需要修改就可以做4个数的全排列(共24种排列)。

完成了上述要求之后再考虑第二个问题:如果再定义一个常量M表示从N个数中取几个数做排列(N == M时表示全排列),原来的程序应该怎么改?

最后再考虑第三个问题:如果要求从N个数中取M个数做组合而不是做排列,就不能用原来的递归过程了,想想组合的递归过程应该怎么描述,编程实现它。

二、参考代码

segmentfault: C语言如何打印一个数组排列组合? https://segmentfault.com/a/1190000000725176

#include #define N 4 #define M 3 int a[N]; void perm(int); void print(); void swap(int, int); int main(void) { int i; int b[N] = {1, 2, 3, 4}; for (i = 0; i < N; i++){ a[i] = b[i]; } perm(0); return 0; } void perm(int offset) { int i; if (offset == N - 1){ print(); return; } else{ for (i = offset; i < N; i++){ swap(i, offset); perm(offset + 1); swap(i, offset); } } } void print() { int i; for (i = 0; i < N; i++) printf("%d", a[i]); printf("\n"); } void swap (int i, int offset) { int temp; temp = a[i]; a[i] = a[offset]; a[offset] = temp; } 三、这篇博文的初衷

先说下写这篇博文的初衷.

题目是《Linux C编程一站式学习》课后练习题里的,老师已经给了解题思路.

看似简单的题目,自己思考了一个多小时,却无从下手,只得在网上搜索答案,然后找到了这段参考代码,当然,相同的代码有很多博主都在引用,我也不知道最初出处,反正功劳都是人家的,和我无关,我只知道运行结果确实是正确的.理解代码费了好几个小时,非常的头大.最后还是耐着性子,在纸上一步一步画出了框架.

这里详细写下程序的运算过程,自己总结经验的同时,希望也能对有相同困扰的其他同学有所帮助.

四、自己的理解

个人认为这段代码的难点在于 perm() 这个函数.其他的都比较好理解,比如:

print(): 依次打印数组 a[N] 中的各元素; swap(int i,int offset): 交换数组中 a[i] 和 a[offse] 这两个元素的位置;

下面详细说明 perm() 这个函数,理解难点在于 perm() 中的 for 循环.

4.1、 perm() 函数

为更好理解,以 4 个元素的数组来说明,即 int a[4] = {1,2,3,4};.

从 main() 函数里的 perm(0)开始,如下图:

在这里插入图片描述

由上图的perm() 函数运行过程可以看出,第一层 for 循环实现的作用是把数组中的第一个元素和后边每个元素依次调换(保证每个元素都有机会排在第一个元素,),第二层 for 循环实现的作用是在第一个元素已经确定的情况下把第二个元素和后边每个元素(第三个、第四个…元素)依次调换,最后一层 for 循环实现的作用把倒数第二个元素和之后的元素(最后一个)调换,此时满足条件 offset == N - 1 即递归的 Base case 基础条件,打印出结果.

第二个问题

问题:如果再定义一个常量M表示从N个数中取几个数做排列(N == M时表示全排列),原来的程序应该怎么改?

代码:

#include #define N 5 #define M 3 int a[N]; void perm(int); void print(); void swap(int, int); int main(void) { int i; int b[N] = {1, 2, 3, 4, 5}; for (i = 0; i < N; i++){ a[i] = b[i]; } perm(0); return 0; } void perm(int offset) { int i; if (offset == M){ //改了这里 print(); return; } else{ for (i = offset; i < N; i++){ swap(i, offset); perm(offset + 1); swap(i, offset); } } } void print() { int i; for (i = 0; i < M; i++) //改了这里 printf("%d", a[i]); printf("\n"); } void swap (int i, int offset) { int temp; temp = a[i]; a[i] = a[offset]; a[offset] = temp; }

改了两个地方:一个是perm()函数,另一个是print()函数.

从第一个问题的解决过程中可以看出,因为只需要取 M 个元素进行排列,可以只让for循环执行到数组的第M个元素,offset==M是终止条件,即进行到第M-1层for循环,而第 M 个元素不会再和之后的元素进行调换.其实此处的offset==M改为offset==N-1,程序依然可以得到正确结果,从print()函数可以看出,只打印了数组的前M个元素,因此,offset=N-1这个条件比offset==M进行了更多的运算,对第M后的元素排序是多余的,多做了一些无用功.

第三个问题 从N个数中取M个数做组合

代码:

#include #define N 5 #define M 3 int a[N], b[M], count = 0; void comb(int, int); void print(); void swap(int, int); int main(void) { int i; int c[N] = {1, 2, 3, 4, 5}; for (i = 0; i < N; i++){ a[i] = c[i]; } count = 0; comb(N, M); return 0; } void comb(int n, int m) { int i; if (m == 0) { print(); return; } else { for (int i = n-1; i >= 0; i--) { b[m-1] = a[i]; //print(); //printf("test\n"); comb(i, m-1); } } } void print() { int i; for (i = 0; i < M; i++) printf("%d", b[i]); printf("\n"); } void swap (int i, int offset) { int temp; temp = a[i]; a[i] = a[offset]; a[offset] = temp; }

为更好理解,以从 5 个元素的数组中取 3 个元素进行组合为例来说明, int a[5] = {1,2,3,4,5};.

代码中共有 3 个 数组;

c[]数组的作用主要用于给数组a[]赋值; 数组a[]中的元素给数组b[]赋值; 打印数组b[]中的元素即可得到结果;

从 main() 函数里的 comb(0)开始,如下图:

在这里插入图片描述

从上图中可以看出,第一层for循环作用是把数组a[]中的元素从后到前(本例中是以5,4,3,2,1的顺序)依次赋值给数组b[]的第M个元素; 第二层for循环作用是把数组a[]中的元素(第二层循环选取的元素只能排在第一层循环选取的元素的前面)赋值给数组b[]的第M-1个元素; … 最后一层for循环作用是把a[]中的元素赋值给数组b[]的第 1 个元素;此时 m==0条件成立,即所谓的 Base case基础条件成立,打印出结果.

其实从上图中,可以看到有些步骤是多余的,comb(i,m-1),当 i < m-1时,最终演变的结果总是 i



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭