剑指offer——字符串的排列

题目描述

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

输入描述:

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

书上的要求是

(1)不要求按字典顺序

(2)可能存在重复的字符串排列

递归思路

固定要排序部分的第一个字符(从该字符到最后一个字符,都可以和第一个字符交换位置,交换完固定第一个字符。也就是让任何字符都有可能出现在第一个位置),后面的字符再全排列。

#include<iostream>  
using namespace std;  
#include<assert.h>  

void Permutation(char* sourceStr, char* changeStr)  
{  
    assert(sourceStr && changeStr);//断言,防止越界  

    if(*changeStr== '\0')  
        printf("%s\n",sourceStr);//如果没有可交换字母,已经到末尾,将源字符串输出 
    else  
    {  
    //比如源字符串当前是sourceStr="abc",交换字符串也为changeStr="abc",
    //需要注意的是,函数void Permutation(str,str)传的是指向同一个字符串的指针
        for(char* pCh = changeStr; *pCh != '\0'; pCh++)  
        {  
            swap(*changeStr,*pCh);//交换第一个和后面一个  
            Permutation(sourceStr, changeStr+1);//固定前面的,后面再排列  
            swap(*changeStr,*pCh);//再换回原来的  
        }  
    }
    //一次完整的打印是这样的,首先sourceStr、changeStr都指向"abc",接着swap第一个,结果还是"abc"
}  

int main(void)  
{  
    char str[] = "abc";  
    Permutation(str,str);  
    return 0;  
}  

后面的swap是,递归调用swap交换前后两个字符,在最后交换完成入List之后再交换回来,回到初始状态再进下一个循环。

在IDE上设置断点,单步调试,对照前面的树图理解。

本问题解答

后去重(遍历时没有考虑相同字符的交换问题)

void Permutation( string &pBegin, int n, vector<string> &res)//n为下标值
{
    int size = pBegin.size();
    if (n == size)
        res.push_back(pBegin);//如果下标值等于字符串的长度,输出字符串
    else
    {
        for (int i = n; i < pBegin.size(); i++)
        {
            char temp = pBegin[i];
            pBegin[i] = pBegin[n];
            pBegin[n] = temp;
            Permutation(pBegin, n + 1, res);//移动下标
            temp = pBegin[i];
            pBegin[i] = pBegin[n];
            pBegin[n] = temp;
        }
    }
}
vector<string> Permutation(string pStr)
{
    vector<string> res;
    if (pStr.size() == 0)
        return res;
    Permutation(pStr, 0, res);
    sort(res.begin(), res.end());//使用unique的使用,要先排序
    res.erase(unique(res.begin(), res.end()), res.end());//unique去重
    //unique()函数将重复的元素放到vector的尾部 然后返回指向第一个重复元素的迭代器 再用erase函数擦除从这个元素到最后元素的所有的元素
    return res;
}
void main()
{
    char str[] = "aab";
    vector<string> res = Permutation(str);
    return;
}

先去重(交换时遇到重复的字符跳过,不交换)

这里一次循环结束的条件是begin位于最后一个位置,可以直接输出字符串了,因为只有一种可能。不必让begin == size时结束。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
    vector<string> Permutation(string str) {
         vector<string> a;
         if(str.empty())
             return a;
         Permutation(a,str,0);
         sort(a.begin(),a.end());//按照字典序输出
         return a;
      }
    void Permutation(vector<string> &array, string str, int begin)//遍历第begin位的所有可能性
    {
        //一次遍历的结束条件
        if(begin == str.size()-1)
        {
            array.push_back(str);
        }
        for(int i=begin;i<str.size();i++)
        {
            if(i!=begin && str[i] == str[begin])
            {
                continue;//有与begin位重复的字符串不进行交换,跳过
            }
            swap(str[i],str[begin]);
            //当i==begin时,也要遍历其后面的所有字符
            //当i!=begin时,先交换,使第begin位取到不同的可能字符,再遍历后面的字符
            Permutation(array,str,begin+1);
            swap(str[i],str[begin]);//为了防止重复的情况,还需要将begin处的元素重新换回来
        }
    }
};

猜你喜欢

转载自blog.csdn.net/eartha1995/article/details/81110742