LeetCode 46. Permutations

问题链接

LeetCode 46. Permutations

题目解析

求解一个无重复元素序列的全排列。

解题思路

什么是全排列?理解一下题意,简单来讲,就是求解序列中元素的所有排列方法,一共有A(n, n)=n!种排列。可以采用递归、非递归、插空等方法求解。

求解全排列参考链接:数组的全排列

注意:本题中已经说明没有重复元素,对于有重复元素的情况,将在47. Permutations II在给大家说明。

DFS求解全排列

采用DFS,大致算法如下:

  • 任意取一个元素放在第一个位置,则有n种选择;
  • 再剩下的n-1个元素中再取一个元素放在第二个位置则有n-1种选择,此时可以看做对n-1个元素进行全排列;
  • 重复第二步,直到对最后一个元素进行全排列,即最后一个元素放在最后一个位置,全排列结束。

此算法容易理解,但是需要耗费大量的栈空间。每次通过交换nums[index]和nums[i],代表当前选择了nums[i],然后进行下一次选择(DFS),参考代码如下:

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

非递归求解全排列

还记得LeetCode 31. Next Permutation吗?这道题中求的是序列的下一个序列,本题中其实就是求所有的“下一个序列”。试想一下,如果我不断地求下一个排列,最终我就可以得到所有的排列。

为了避免重复,先将数组排个序,当数组完全逆序时退出循环。关于如何求解全排列,在LeetCode 31. Next Permutation也有具体的过程,这里就不多说了。

直接借用库函数,投机取巧之法:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector< vector<int> > res;
        sort(nums.begin(), nums.end());
        do {
            res.push_back(nums);
        }while(next_permutation(nums.begin(), nums.end()));
        return res;
    }
};

具体实现全排列,参考代码如下:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector< vector<int> > res;
        sort(nums.begin(), nums.end());
        while(1) {
            res.push_back(nums);
            int n = nums.size(), i = n-2, j = n-1;
            while(i >= 0 && nums[i] >= nums[i+1]) i--;
            if(i >= 0) {
                while(nums[i] >= nums[j]) j--;
                swap(nums[i], nums[j]);
            }
            else break;
            reverse(nums.begin()+i+1, nums.end());
        }
        return res;
    }
};

插空法求解全排列

参考大神的帖子:Permutations。了解到这么一种巧妙的方法。

  • 当n=1时,数组中只有一个数a1,其全排列只有一种,即为a1;
  • 当n=2时,数组中此时有a1a2,其全排列有两种,a1a2和a2a1,那么此时我们考虑和上面那种情况的关系,我们发现,其实就是在a1的前后两个位置分别加入了a2;
  • 当n=3时,数组中有a1a2a3,此时全排列有六种,分别为a1a2a3, a1a3a2, a2a1a3, a2a3a1, a3a1a2和 a3a2a1。那么根据上面的结论,实际上是在a1a2和a2a1的基础上在不同的位置上加入a3而得到的。_ a1 _ a2 _ : a3a1a2, a1a3a2, a1a2a3。_ a2 _ a1 _ : a3a2a1, a2a3a1, a2a1a3。

以此类推,可以求得任意n元素的全排列。代码中每次递归取出原数组第一个元素并删除之,利用words保存当前所有排列,然后每个排列中在各个位置插入该元素,参考代码如下:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.empty()) return vector< vector<int> >(1, vector<int>());
        
        vector< vector<int> > res;
        int first = nums[0];
        nums.erase(nums.begin());
        vector< vector<int> > words = permute(nums);
        for(auto &a : words) {
            for(int i = 0; i <= a.size(); ++i) {
                a.insert(a.begin() + i, first);
                res.push_back(a);
                a.erase(a.begin() + i);
            }
        }   
        return res;
    }
};

相似题目

LeetCode 31. Next Permutation


LeetCode All in One题解汇总(持续更新中...)

本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.


posted @ 2018-03-30 00:36  AlvinZH  阅读(304)  评论(0编辑  收藏  举报