博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面试试题 一 (排列组合)
阅读量:5273 次
发布时间:2019-06-14

本文共 7306 字,大约阅读时间需要 24 分钟。

昨天有个同学去面试了,回来讨论关于面试的题,因为是提前批次的,所以有点难,有点绕,自己写总结了一下:

1.此题是有关24点游戏,(整体的实现在),其中用到了递归,比较重要的知识点是排列组合,针对此题,简单说就是4(n)个元素,按不同的排列组合方式,有4!(n! 也就是A(n,n))种方式。都说迭代的人,递归的是神,先利用递归进行实现:(自己觉得代码很简单明了)

#include
#include
#include
using namespace std;void print(vector
&arr) //只是简单打印每次输出的结果{ for (const auto c : arr) cout << c << " "; cout << endl;}void perm(size_t t, vector
&arr, int &cnt) //核心算法,进行排列组合,t从0开始,cnt计算有多少排列方式{ if (t >= arr.size()) { print(arr); cnt++; return; } for (size_t i = t; i < arr.size(); i++) { if (i != t ) swap(arr[i], arr[t]); perm(t + 1,arr,cnt); //表示对剩下的元素进行排列组合,perm{b,c,d}/ perm{a,c,d}/ perm{b,a,d}/ perm{b,c,a} if (i != t) swap(arr[i], arr[t]); //表示交换之后再交换回来,要在原有的基础上(a,b,c,d)进行交换 }} int main() //测试{ static int cnt=0; vector
arr{ "a", "b" , "c", "d"}; perm(0,arr,cnt); cout << "共有" << cnt <<"种排列组合方法!"<< endl; return 0;}

      这里详细讲解下有关递归方法实现原理:n个元素的排列组合表示为perm{x1,x2,x3....,xn},当有4个元素(a, b, c, d)时,则表示为perm{a, b, c, d},按递归的方法则perm{a,b,c,d}=a.perm{b,c,d}+b.perm{a,c,d}+c.perm{b,a,d}+d.perm{b,c,a},  这里加号表示并集的意思,对于为什么是c.perm{b,a,d}而不是c.perm{a,b,d},从代码中swap()函数可以得到它是交换了a和c,同理d.perm{b,c,a}也是交换了a和d。

    代码中有两个swap()函数,是因为总是在原有的组合上(a,b,c,d)进行排列交换,具体的过程为:第一轮:开始i=t=0,不交换,也就是a.perm{b,c,d}(递归对b,c,d进行排列组合),执行完后,不交换;第二轮:交换a和b(swap(arr[1],arr[0])),所以为b. perm{a,c,d}(执行递归),之后再交换回来(swap(arr[1],arr[0]))则恢复a,b,c,d;第三轮:交换a和c(swap(arr[2],arr[0]),是在a,b,c,d的基础上进行交换(第三轮已经恢复回来了)),所以为c.perm{b,a,d}(执行递归),之后再交换回来(swap(arr[2],arr[0]))为a.b.c.d;第四轮:交换a和d (swap(arr[3],arr[0]),也是在a,b,c,d的基础上进行的交换),为d.perm{b,c,a}(执行递归),之后再交换回来(swap(arr[3],arr[0])),依然为a,b,c,d,因此执行完整个代码,输入的vector<string> arr依然是保持不变的(为a,b,c,d)。

perm{a,b,c,d}=a.perm{b,c,d}+b.perm{a,c,d}+c.perm{b,a,d}+d.perm{b,c,a}

 =a.b.perm{c,d}+a.c.perm{b,d}+a.d.perm{c,b}+b.a.perm{c,d}+b.c.perm{a,d}+b.d.perm{c,a}+c.b.perm{a,d}+c.a.perm{b,d}+c.d.perm{a,b}+d.b.perm{c,a}+d.c.perm{b,a}+d.a.perm{c,b}

= a.b.c.d+a.b.d.c+a.c.b.d+a.c.d.b+a.d.c.b+a.d.b.c+

   b.a.c.d+b.a.d.c+b.c.a.d+b.c.d.a+b.d.c.a+b.d.a.c+

   c.b.a.d+c.b.d.a+c.a.b.d+c.a.d.b+c.d.a.b+c.d.b.a+

   d.b.c.a+d.b.a.c+d.c.b.a+d.c.a.b+d.a.c.b+d.a.b.c 

验证后和代码运行结果一致的。。

2 .简单迭代实现,穷举法,虽然迭代看起来逻辑简单,但是时间复杂度很高,当待排列的数目发生变化时,这种方法要改变程序for循环的层数。(程序没有一般性)

#include
using namespace std;/*这里用迭代实现的排列组合,因为有四个数,所以有四层迭代,也就是说每个位置的数都可以为1,2,3,4 ,但是4个位置同时不能有相等的数出现*//*缺点是当待排列组合的数越来越多,则时间复杂度会很高为n^4*/void perm(int arr[],int &cnt) //这里有四层循环{ for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (j == i) //i不能等于j continue; for (int k = 0; k < 4; k++) { if (k == i || k == j) //k不能等于i,也不能等j continue; for (int l = 0; l < 4; l++) { if (l==i||l==j||l==k) continue; cout << arr[i] << ' ' << arr[j] << ' ' << arr[k] << ' ' << arr[l] << endl; cnt++; } } } } }int main(){ int arr[4] = { 1, 2, 3, 4 }; static int cnt = 0; perm(arr,cnt); cout << "共有" << cnt << "种方法!" << endl; return 0;}

3.对第二种迭代方法进行优化,循环变为三层,循环的次数正好等于排列组合的方法数。(也是待排序的数目发生变化时要改变程序,程序也没有一般性)

#include
using namespace std;/*这里用迭代实现的排列组合,对上面的迭代方法进行优化,能减少时间复杂度*//*但是仍然是当待排列组合的数越来越多,则时间复杂度会变高(为排列组合的方法数,这里为4!=24)*/void perm(int arr[], int &cnt) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 4; k++) //当求余4时,结果都是在0,1,2,3之间,刚好为数组的下标 { cout << arr[k % 4] << ' ' << arr[(k + j % 3 + 1) % 4] << ' '; cout << arr[(k + (j + i % 2 + 1) % 3 + 1) % 4] << ' '; cout << arr[(k + (j + (i + 1) % 2 + 1) % 3 + 1) % 4] << endl; //arr[j%3]+" "+arr[(j+i%2+1)%3]+" "+arr[(j+(i+1)%2+1)%3] 3个数的情况 cnt++; } } }}int main(){ int arr[4] = { 0, 1, 2, 3 }; static int cnt = 0; perm(arr, cnt); cout << "共有" << cnt << "种排列组合方法!" << endl; return 0;}

 4,当从一个数组中取m个数字,有多少种取法,利用穷举法,这里假设从一个数组中取3个数字。(此处为C(8,3),与上面不同的是取出来的数字不讲究顺序,也就是C(n,m),从n个元素中取m个元素有多少种取法(m<=n)),程序没有一般性。

#include
#include
using namespace std;/*穷举法*/void combination(const vector
& arr, int &cnt) //这里是三层循环,是从数组中取三个数,要是取4个数,程序就改成4层循环{ for (size_t i = 0; i < arr.size() - 2; i++) //直接改为i
arr { 1, 2, 3, 4,5,6,7,8}; static int cnt = 0; combination(arr, cnt); cout << "共有" << cnt << "种组合方法!" << endl; return 0;}

5、对于问题4,在n个元素中取m个元素共有C(n,m)中取法,利用递归实现。

#include
#include
using namespace std;/*递归法,从n个元素中取m个元素,有C(n,m)种取法,这里index[]存放取出来的m个元素;k为要取得元素个数,开始为m,后来为m-1,m-2,...,1;m为取出元素的个数,作用是打印取出的元素数组index;size为总元素个数n ; cnt统计多少种组合方式*/void combine(vector
&arr, int index[], int k, const int m, int size,int &cnt) { for (int i = size - 1; i >= k - 1; --i) { index[k - 1] = arr[i]; if (k > 1) //当取出的元素个数为1时,则不再递归 { combine(arr, index, k - 1, m, i,cnt); } else { for(int i = 0; i < m; ++i) cout << index[i]; cnt++; cout << endl; } } } void combination(vector
&arr, int size, int m,int & cnt) { if(m > size) return; int* out = new int[m]; combine(arr,out,m,m,size,cnt); delete [] out; } int main(){ vector
arr {
1,2,3,4,5,6,7,8}; static int cnt = 0; combination(arr, arr.size(),3,cnt); //这里是取3个元素 cout << "共有" << cnt << "种组合方法!" << endl; return 0;}

例如从5个元素{1,2,3,4,5}取3个元素,则取一个元素后,问题简化为从剩下的取2个元素,再取走一个元素后,则简化为从剩下的元素中取一个元素。

具体过程为:这里是从最后一个元素开始,例如从5个元素{1,2,3,4,5}取3个元素:则从i=size-1=4开始(因为下标都是从零开始),i>=2,k为3,输出的index[2]=5,

再递归从{1,2,3,4}中取2个元素i=3开始,i>=1,k为2,输出index[1]=4,

再递归从{1,2,3}中取一个元素,i=2开始,i>=0,k为1,此时是指取出一个元素,不再递归,index[0]=3/2/1,则依次输出3,4,5; 2,4,5; 1,4,5;

返回至上一层--i为2,从{1,2,3,4}中取出2个元素,此时index[1]=3,递归下去,同理从{1,2}中取一个元素,则不再递归,依次输出2,3,5; 1,3,5;

再返回上一层--i为1,从{1,2,3,4}中取出2个元素,此时index[1]=2,递归下去,同理从{1}中取一个元素,不再递归,输出1,2,5;

同理,index[2]=4时,index[1]=3输出为2,3,4;1,3,4;index[1]=2,输出1,2,4;index[2]=3时,index[1]=2输出为1,2,3。

补充:6 . 对问题1,要从n个元素中选择m个元素,也就是有A(n,m)中排列方法,在1的基础上修改:

#include
#include
using namespace std;void perm(size_t t, vector
&arr,const int m, int &cnt) //核心算法,进行排列组合,t从0开始,m为取出的元素个数,cnt计算有多少排列方式,{ if (t >= m) { for (int j=0;j!=m;j++) cout << arr[j]<< " "; cnt++; cout << endl; return; } for (size_t i = t; i < arr.size(); i++) { if (i != t ) swap(arr[i], arr[t]); perm(t + 1,arr,m,cnt); if (i != t) swap(arr[i], arr[t]); //表示交换之后再交换回来,要在原有的基础上进行交换 }} int main() //测试{ static int cnt=0; vector
arr{
1,2,3,4,5}; perm(0,arr,3,cnt); //这里是取3个元素 cout << "共有" << cnt <<"种排列组合方法!"<< endl; return 0;}

 

转载于:https://www.cnblogs.com/liuamin/p/6940342.html

你可能感兴趣的文章
Python 系统性能信息模块psutil
查看>>
python 简单的爬虫
查看>>
Log4net介绍
查看>>
Windows Azure Cloud Service (7) Windows Azure项目文件详解
查看>>
101. 对称二叉树
查看>>
TEST ON 平安夜
查看>>
python中的小知识点
查看>>
个人总结-9-session的使用,十天免登陆
查看>>
re库
查看>>
javascript变量精度的处理
查看>>
快速下载android源码
查看>>
unity UGUI动态滑动列表
查看>>
[CODEVS1697]⑨要写信
查看>>
PHP面向对象编程快速入门
查看>>
阶段一:用Handler和Message实现计时效果及其中一些疑问
查看>>
Ubuntu16.04中nginx除80之外其他端口不能访问
查看>>
Java中HashMap和TreeMap的区别深入理解
查看>>
【学习笔记】Silverlight框架:Jounce(6)——Command和ViewModel
查看>>
Java设计模式总汇一 (适配器、单例、静态代理、简单工厂设计模式)
查看>>
异步FIFO的FPGA实现
查看>>