剑指offer 38 字符串的排列(递归)

字符的全排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

思路:递归的思路

1、求所有可能出现在第一个位置的字符,即把第一个字符和后面的所有字符交换

        2、固定第一个字符求后面所有字符的排列

最后res的序列是:abc,acb,bac,bca,cab,cba

递归代码:

class Solution {
public:
    vector Permutation(string str) {
        vectorst;
        if(str.size()==0)
            return st;
        Permutation(st,str,0);
        sort(st.begin(),st.end());
        return st;
    }
     void Permutation(vector &array, string str, int begin)//遍历第begin位的所有可能性
    {
        if(begin==str.size()-1)
            array.push_back(str);//只剩一个元素未排列时
        for(int i=begin; i

字符的组合

题目:输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。

上面我们详细讨论了如何用递归的思路求字符串的排列。同样,本题也可以用递归的思路来求字符串的组合。

 

 

思路:同样是用递归求解。可以考虑求长度为n的字符串中m个字符的组合,设为C(n,m)。原问题的解即为C(n, 1), C(n, 2),...C(n, n)的总和。对于求C(n, m),从第一个字符开始扫描,每个字符有两种情况,要么被选中,要么不被选中,如果被选中,递归求解C(n-1, m-1)。如果未被选中,递归求解C(n-1, m)。不管哪种方式,n的值都会减少,递归的终止条件n=0或m=0。

//函数功能 : 从一个字符串中选m个元素  
//函数参数 : pStr为字符串, m为选的元素个数, result为选中的  
//返回值 :   无  
void Combination_m(char *pStr, int m, vector &result)  
{  
    if(pStr == NULL || (*pStr == '\0'&& m != 0))  
        return;  
    if(m == 0) //递归终止条件  
    {  
        for(unsigned i = 0; i < result.size(); i++)  
            cout< result;  
        Combination_m(pStr, i, result);  
    }  
}  

字符的子集

描述

给定一个含不同整数的集合,返回其所有的子集

子集中的元素排列必须是非降序的,解集必须不包含重复的子集

您在真实的面试中是否遇到过这个题?  是

样例

如果 S = [1,2,3],有如下的解:

 
  1. [

  2. [3],

  3. [1],

  4. [2],

  5. [1,2,3],

  6. [1,3],

  7. [2,3],

  8. [1,2],

  9. []

  10. ]

挑战

你可以同时用递归与非递归的方式解决么?

递归的方法:
不要去想递归是如何一层一层调用的,而是要想如果解决当前问题,找到递归的出口

和字符串的排列一样,将数组分为第一个元素和后面的所有的元素,此时面临取第一个元素,递归剩余的元素或者不取第一个元素(即直接递归剩余元素)

代码如下:
 

class Solution {
public:
    /**
     * @param nums: A set of numbers
     * @return: A list of lists
     */
    vector> subsets(vector &nums) {
        // write your code here
       //这里排序并不必须,只是为了子集中元素以递增排序
        sort(nums.begin(), nums.end());
        vector> subs;
        vector sub;  
        genSubsets(nums, 0, sub, subs);
        return subs; 
    }
    void genSubsets(vector& nums, int start, vector& sub, vector>& subs) {
        //我们发现这里好像并没有递归出口,因为终止条件由下面for循环控制
        subs.push_back(sub);        
        for (int i = start; i < nums.size(); i++) {
            //情况一:子集包含元素nums[i]
            sub.push_back(nums[i]);
            //为何是i+1,因为已经解决了第i个元素,需要递归从第i+1个元素开始求解
            genSubsets(nums, i + 1, sub, subs);
            //情况二:子集不包含nums[i],即略过第i个元素
            //可以想象,不去管上一条递归语句,当下一次循环到i+1时,第i个元素已经略过
            sub.pop_back();
        }
    }
};

 

题目:输入两个整数n和m,从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合。

 

#include   
#include   
using namespace std;  
list list1;  
void find_factor(int sum,int n)  
{  
    //递归出口  
    if(n<=0||sum<=0)  
        return;  
    //输出找到的数  
    if(sum==n)  
    {  
        list1.reverse();  
        for(list::iterator iter=list1.begin();iter!=list1.end();iter++)  
            cout<<*iter<<"+";  
        cout<>sum>>n;  
    cout<<"所有可能的序列,如下:"<


 

字符串全排列扩展----八皇后问题
    题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。

    这就是有名的八皇后问题。解决这个问题通常需要用递归,而递归对编程能力的要求比较高。因此有不少面试官青睐这个题目,用来考察应聘者的分析复杂问题的能力以及编程的能力。

由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标i和j,是不是i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]。

关于排列的详细讨论,详见上面的讲解。
接下来就是写代码了。思路想清楚之后,编码并不是很难的事情。下面是一段参考代码:

#include  
using namespace std;  
  
int g_number = 0;  
void Permutation(int * , int  , int );  
void Print(int * , int );  
  
void EightQueen( )  
{  
    const int queens = 8;  
    int ColumnIndex[queens];  
    for(int i = 0 ; i < queens ; ++i)  
        ColumnIndex[i] = i;    //初始化  
    Permutation(ColumnIndex , queens , 0);  
}  
  
bool Check(int ColumnIndex[] , int length)  
{  
    int i,j;  
    for(i = 0 ; i < length; ++i)  
    {  
        for(j = i + 1 ; j < length; ++j)  
        {  
            if( i - j == ColumnIndex[i] - ColumnIndex[j] || j - i == ColumnIndex[i] - ColumnIndex[j])   //在正、副对角线上  
                return false;  
        }  
    }  
    return true;  
}  
void Permutation(int ColumnIndex[] , int length , int index)  
{  
    if(index == length)  
    {  
        if( Check(ColumnIndex , length) )   //检测棋盘当前的状态是否合法  
        {  
            ++g_number;  
            Print(ColumnIndex , length);  
        }  
    }  
    else  
    {  
        for(int i = index ; i < length; ++i)   //全排列  
        {  
            swap(ColumnIndex[index] , ColumnIndex[i]);  
            Permutation(ColumnIndex , length , index + 1);  
            swap(ColumnIndex[index] , ColumnIndex[i]);  
        }  
    }  
}  
  
void Print(int ColumnIndex[] , int length)  
{  
    printf("%d\n",g_number);  
    for(int i = 0 ; i < length; ++i)  
        printf("%d ",ColumnIndex[i]);  
    printf("\n");  
}  
  
int main(void)  
{  
    EightQueen();  
    return 0;  
}  
 

 

 

你可能感兴趣的:(剑指offer)