字典序全排列递归总结:值传递与引用传递

问题:

给定一个具有n个元素的集合(n>=1),要求输出这个集合中元素的所有可能的排列。
Given a collection of distinct numbers, return all possible permutations.
For example,
[1,2,3] have the following permutations:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

不是字典序的递归解法:

1)把字符串分成两部分,第一部分是字符串的第一个字符,剩下的所有部分是第二个字符,假设已经得到第二部分字符串的排列;
2)用第一个字符与它后面的字符进行逐个交换。

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ans;
        int n=nums.size();
        dfs(ans,nums,0);
        return ans;
    }
    void dfs(vector<vector<int>> & ans,vector<int>& nums,int i){
        int n=nums.size();
        if(i>=n){
            ans.push_back(nums);
            return;
        }else{
            for(int j=i;j<n;j++){
                swap(nums[j],nums[i]);
                dfs(ans,nums,i+1);
                swap(nums[j],nums[i]);//重置到原始顺序
            }
        }
    }
};

我们这里用例子[1,2,3]分析以上程序的递归过程:

dfs(0, {1,2,3})
i=0时,j=0,1,2

j=0时,{1,2,3}
|-swap(i,j) ->{1,2,3}
|-dfs(1,{1,2,3})———-i=1时,j=1,2
|———————————1) j=1时,{1,2,3}
|————————————|-swap(i,j) ->{1,2,3}
|————————————|-dfs(2,{1,2,3})——–i=2时,j=2
|————————————|——————————swap(i,j) -> {1,2,3}
|————————————|——————————dfs(3,{1,2,3})//输出{1,2,3}
|————————————|——————————swap(i,j) ->{1,2,3}
|————————————|-swap(i,j) ->{1,2,3}
|———————————2) j=2时,{1,2,3}
|————————————|-swap(i,j)-> {1,3,2}
|————————————|-dfs(2,{1,3,2})——–i=2时,j=2
|————————————|——————————swap(i,j) -> {1,3,2}
|————————————|——————————dfs(3,{1,3,2})//输出{1,3,2}
|- ———————————-|——————————swap(i,j) -> {1,3,2}
|————————————|-swap(i,j)-> {1,2,3}
|-swap(i,j) ->{1,2,3}

j=1时,{1,2,3}
|-swap(i,j) ->{2,1,3}
|-dfs(1,{2,1,3})———-i=1时,j=1,2
|- ——————————–1) j=1时,{2,1,3}
|- ———————————-|-swap(i,j)-> {2,1,3}
|- ———————————–|-dfs(2,{2,1,3})——–i=2时,j=2
|- ———————————–|——————————swap(i,j) -> {2,1,3}
|- ———————————–|——————————dfs(3,{2,1,3})//输出{2,1,3}
|- ———————————–|——————————swap(i,j) ->{2,3,1}
|- ———————————–|swap(i,j) ->{2,1,3}
|- ——————————-2) j=2时,{2,1,3}
|- ———————————–|-swap(i,j)-> {2,3,1}
|- ———————————–|-dfs(2,{2,3,1})——–i=2时,j=2
|- ———————————–|——————————swap(i,j) -> {2,3,1}
|- ———————————–|——————————dfs(3,{2,3,1})//输出{2,3,1}
|- ———————————–|——————————swap(i,j) -> {2,1,3}
|- ———————————–|-swap(i,j)-> {2,1,3}
|- swap(i,j) ->{1,2,3}

j=2时,{1,2,3}
|-swap(i,j) ->{3,2,1}
|-dfs(1,{3,2,1})———-i=1时,j=1,2
|- ——————————–1) j=1时,{3,2,1}
|- ———————————-|-swap(i,j)-> {3,2,1}
|- ———————————–|-dfs(2,{3,2,1})——–i=2时,j=2
|- ———————————–|——————————swap(i,j) -> {3,2,1}
|- ———————————–|——————————dfs(3,{3,2,1})//输出{3,2,1}
|- ———————————–|——————————swap(i,j) ->{3,2,1}
|- ———————————–|swap(i,j) ->{3,2,1}
|- ——————————-2) j=2时,{3,2,1}
|- ———————————–|-swap(i,j)-> {3,1,2}
|- ———————————–|-dfs(2,{3,1,2})——–i=2时,j=2
|- ———————————–|——————————swap(i,j) -> {3,1,2}
|- ———————————–|——————————dfs(3,{3,1,2})//输出{3,1,2}
|- ———————————–|——————————swap(i,j) -> {3,1,2}
|- ———————————–|-swap(i,j)-> {3,2,1,}
|- swap(i,j) ->{1,2,3}

所以,[1,2,3]通过上面的程序输出结果:

[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,2,1],[3,1,2]]

注意:void dfs(vector< vector> & ans, vector< int >& nums, int i);
这里nums在函数中的传参方式是引用
这里不是字典序的,从[3,2,1],[3,1,2]看出。

结果是字典序的解法:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ans;
        int n=nums.size();
        dfs(ans,nums,0);
        return ans;
    }
    void dfs(vector<vector<int>> & ans,vector<int> nums,int i){
        int n=nums.size();
        if(i>=n){
            ans.push_back(nums);
            return;
        }else{
            for(int j=i;j<n;j++){
                swap(nums[j],nums[i]);
                dfs(ans,nums,i+1);
            }
        }
    }
};

也同样用例子[1,2,3]分析递归过程:

dfs(0, {1,2,3})
i=0时,j=0,1,2

j=0时,{1,2,3}
|-swap(0,0) ->{1,2,3}
|-dfs(1,{1,2,3})———-i=1时,j=1,2
|———————————1) j=1时,{1,2,3}
|————————————|-swap(1,1) ->{1,2,3}
|————————————|-dfs(2,{1,2,3})——–i=2时,j=2
|————————————|——————————swap(2,2) -> {1,2,3}
|————————————|——————————dfs(3,{1,2,3})//输出{1,2,3}
|———————————2) j=2时,{1,2,3}
|————————————|-swap(1,2)-> {1,3,2}
|————————————|-dfs(2,{1,3,2})——–i=2时,j=2
|————————————|——————————swap(i,j) -> {1,3,2}
|————————————|——————————dfs(3,{1,3,2})//输出{1,3,2}

j=1时,{1,2,3}
|-swap(0,1) ->{2,1,3}
|-dfs(1,{2,1,3})———-i=1时,j=1,2
|- ——————————–1) j=1时,{2,1,3}
|- ———————————-|-swap(1,1)-> {2,1,3}
|- ———————————–|-dfs(2,{2,1,3})——–i=2时,j=2
|- ———————————–|——————————swap(2,2) -> {2,1,3}
|- ———————————–|——————————dfs(3,{2,1,3})//输出{2,1,3}
|- ——————————-2) j=2时,{2,1,3}
|- ———————————–|-swap(1,2)-> {2,3,1}
|- ———————————–|-dfs(2,{2,3,1})——–i=2时,j=2
|- ———————————–|——————————swap(2,2) -> {2,3,1}
|- ———————————–|——————————dfs(3,{2,3,1})//输出{2,3,1}

j=2时,{2,1,3}
|-swap(0,2) ->{3,1,2}
|-dfs(1,{3,1,2})———-i=1时,j=1,2
|- ——————————–1) j=1时,{3,1,2}
|- ———————————-|-swap(1,1)-> {3,1,2}
|- ———————————–|-dfs(2,{3,1,2})——–i=2时,j=2
|- ———————————–|——————————swap(2,2) -> {3,1,2}
|- ———————————–|——————————dfs(3,{3,1,2})//输出{3,1,2}
|- ——————————-2) j=2时,{3,1,2}
|- ———————————–|-swap(1,2)-> {3,2,1}
|- ———————————–|-dfs(2,{3,2,1})——–i=2时,j=2
|- ———————————–|——————————swap(2,2) -> {3,2,1}
|- ———————————–|——————————dfs(3,{3,2,1})//输出{3,2,1}

所以,[1,2,3]通过上面的程序输出结果:

[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

注意:

这里的nums的传递参数的方式是值传递,函数内对nums的改变不会影响原来的值。
所以,这里j=1时,nums交换后为[2,1,3],接着的dfs调用对这里的nums不会有任何影响,当循环到j=2后,此时的初始nums=[2,1,3],交换后变成[3,1,2]。

分析为什么这种方法得到的结果是符合字典序的:

已知前提:初始时给定的序列就是有序的,即符合 [1,2,3,…..,n]
第一步:
当i=1时,它为[1,2,3,4,….]也是有序的,用同样方法子问题得到字典序的全排列即可。
当i=2时,交换变为[2,1,3,4,….],它后面部分也是有序的;
当i=3时,交换变为[3,1,2,4,….],它后面部分也是有序的;
…….
归纳:
假设:
当i=k时,首位与第k-1为进行交换,变为[k,2,3,…,k-1,k+1,…],此时它的第k-1位是k-1,该序列的除第一位的部分都是有序的;
则:
当i=k+1时,[k,2,3,…,k-1,k+1,…]的首位与第k为进行交换,变为[k+1,2,3,…,k-1,k,k+2,…],
可以看出[2,3,…,k-1,k,k+2,…]是有序的。
总之,当第一位按从1到n取时,每次它后面的序列都是有序的,保证了得到的序列是有序的。

点赞