剑指Offer28字符串的排列(递归和非递归实现)扩展有重复元素的排列,字符串的组合种类
来源:互联网 发布:2017优化重组数学答案 编辑:程序博客网 时间:2024/05/16 02:56
题目:
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出有字符abc所能排列出来的所有字符串,abc、acb、bac、bca、cab、cba
分析:
这是一个典型的全排列的问题,那么就是典型的深搜递归的题。可以这么分析:求整个字符串的排列,可以看成两部:首先求所有可能出现的第一个位置的字符,即把第一个字符和后面所有的字符字符交换。第二步,固定第一个字符,求后面所有字符的排列。这个时候我们仍把后面的所有字符分成两部分:后面字符的第一个字符,以及这个字符之后的所有字符。然后把第一个字符逐一和它后面的字符交换。
void Permutation(char *pStr){ if(pStr == NULL) return ; Permutation(pStr,pStr);}void Permutation(char *pStr,char *index){ if(*index == '\0') { printf("%s\n",pStr); return ; } else { for(char *ch = index;*ch != '\0';++ch) { char temp = *ch; *ch = *index; *index = temp; Permutation(pStr,index+1); temp = *ch; *ch = *index; *index = temp; } }}
当然,我们也可以用非递归的思路来解决这个问题,这里有一种求解方法,我是从网上看到的,觉得非常经典。下面来看它的思路
只要求出当时全排列的下一个排列,此后以此类推就可以了。那么怎么求下一个排列,这里给了一种方法,我们先来看一下。“926520”这个字符串,我们从后面向前看,先看“20”从左往右看并不是递增的,那么再往前看,“52”,也不是递增的,再往前看“65”,还不是再往前看,“26”此时是递增的了,那么定位到2,然后从最后一个开始遍历,找到第一个比2大的数,我们看到0比2小,那么往前找,2等于2,再往前看5比2大,将5和2交换,得到”956220“然后我们将从6开始往后的字符串翻转,得到“950226”,那么这个就是下个字符串。
看懂了转化过程,我们就能很轻松的写出代码来了。
void Reverse(char *pBegin,char *pEnd){ while(pBegin < pEnd) swap(*pBegin++,*pEnd--);}bool Next_Permutation(char *pStr){ int length = strlen(pStr); char *index = pStr+(length-1); char *End = pStr+(length-1); ///如果是空串 if(*index == *pStr) return false; while(*index != *pStr) { char *index_next = index; index --; if(*index < *index_next) { char *real = End; while(*real < *index) real--; swap(*index,*real); ///将替换之后的所有数翻转 Reverse(index_next,End); return true; } } Reverse(pStr,End); return false;}
那么我接下来扩展一下这个题,如果说这个题目中要求说有重复元素,那么它的排列怎么来求呢?
其实我们可以分析一下。
比如字符串是“122”,根据上面咱们的思路应该是第二个数和第三个数交换,我们会发现还是“122”,也就是重复了,所以我们应该避免出现这种情况。那么我们现在应该这个考虑,找到要被替换的那个位置,一直往后直到那个替换的数的位置,这期间如果有和替换的数一样的话,那么这次就不替换。比如当前字符串“212”,那么它的下一个字符串应该是1和第三个2交换,这期间没有和1一样的数,那么就可以交换,但是当下一次交换的时候,就应该是第一2和第二个2交换了,这俩数一样,所以这趟的交换就可以免了。
下面看代码:
bool IsSwap(char *pBegin,char *End){ char *sh; for(sh = pBegin;sh < End;sh++) { if(*sh == *End) return false; } return true;}void PermutationChongfu(char *pStr,char *index){ if(*index == '\0') { printf("%s\n",pStr); return ; } else { for(char *ch = index;*ch != '\0';ch++) { if(IsSwap(index,ch)) { swap(*index,*ch); PermutationChongfu(pStr,index+1); swap(*index,*ch); } } }}
字符串的组合问题:
输入三个字符abc,他们的组合有a b c ab ac bc abc
这里可以把这些字符串的组合用地归来处理
这样分析:
这一堆字符串,先从中找到一个,那么接下来就有两种处理方式,1是我可以把它放进我的容器里,然后再从剩下的n-1里拿出m-1个。2是我不将其放进容器,那么接下来就要在剩下的n-1里面找出m个数放进容器了。
代码可以这么实现:
void Combination(char *pStr){ if(pStr == NULL) return ; int length = strlen(pStr); vector<char> result; for(int i = 1;i<=length;i++) Combination(pStr,i,result);}void Combination(char *pStr,int num ,vector<char> &result){ if(num == 0) { vector<char>::iterator iter = result.begin(); for(;iter != result.end();iter++) printf("%c ",*iter); cout<<endl; return ; } if(*pStr == '\0') return ; result.push_back(*pStr); Combination(pStr+1,num-1,result); result.pop_back(); Combination(pStr+1,num,result);}
题目扩展:
题目:
输入两个整数n和m,从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合。
分析:
其实这个题前面的题很像,从后面开始遍历,如果把这个数加进容器,那么整个数的和就减去这个数,并且让这个数减1,如果不加进容器的话,那么和是不变的,只要把数减去1就行了。
下面看代码:
void find_factor(int sum,int n){ if(sum < 0 || n < 0) return ; if(sum == n) { result.reverse(); list<int>::iterator iter = result.begin(); for(;iter != result.end();iter++) printf("%d ",*iter); cout<<n<<endl; result.reverse(); } result.push_back(n); find_factor(sum-n,n-1); result.pop_back(); find_factor(sum,n-1);}
- 剑指Offer28字符串的排列(递归和非递归实现)扩展有重复元素的排列,字符串的组合种类
- 【剑指offer28】字符串的全排列和组合
- 字符串的全排列和组合递归非递归--排列组合扩展问题
- 字符串的全排列和组合递归非递归--排列组合扩展问题
- 剑指offer28--字符串的排列
- 剑指offer28:字符串的排列
- 字符串全排列的递归和非递归实现
- 字符串的全排列和组合算法(递归非递归)
- 输出字符串的所有全排列(递归法和非递归,非递归采用组合数学的字典序)
- 8594 有重复元素的排列问题(递归、字符串匹配)
- 字符串的递归排列实现
- 剑指offer28-字符串的全排列
- 【面试题】剑指offer28--字符串的排列
- JAVA递归和非递归输出字符串的全排列
- 字符串排列递归和非递归实现
- 面试题------全排列的非递归和递归实现(含重复元素)
- 全排列的简单递归方法(有重复元素和无重复元素的递归算法)
- 字符串-字符串的全排列-递归实现
- Qt 的QString类的使用
- openCV 操作像素矩阵
- 傻傻的创业者(上)
- 大型网站架构演变和知识体系
- 大数
- 剑指Offer28字符串的排列(递归和非递归实现)扩展有重复元素的排列,字符串的组合种类
- 【算法导论】矩阵链乘法
- realloc函数用法
- 内存对齐
- sqlite 支持的数据类型 全面
- 【git学习一】git的原理
- js对字符的验证
- 基于verilog的曼彻斯特编译码
- linux博客