15 全排列

原题网址:https://www.lintcode.com/zh-cn/problem/permutations/#

给定一个数字列表,返回其所有可能的排列。

 注意事项

你可以假设没有重复数字。

样例

给出一个列表[1,2,3],其全排列为:

[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]
挑战 

使用递归和非递归分别解决。

标签 
 
这道题没想出来,参考了网上的答案,链接:lintcode 求全排列(15)

法1.递归解法:

例如,nums=[1,2,3,4]求全排列,有四个位置

当第一个元素为1时,相当于求2,3,4的全排列。而在此排列中,当2为第一个元素时,相当于求3,4的全排列,当3为第一个元素时,……重复……然后又可以继续划分,3为第一个元素,4本身为一个全排列,4为第一个元素,3本身为一个全排列,此趟over;

当第一个元素为2时,相当于求1,3,4的全排列,而在此排列中,当1为第一个元素时,相当于求3,4的全排列,当3为第一个元素时,……重复……然后又可以继续划分,3为第一个元素,4本身为一个全排列,4为第一个元素,3本身为一个全排列,此趟over;

...................

上述过程中,很容易发现,该过程适用于递归,每趟都是一个递归

 
 

算法理解的差不多了,但是要看懂并复现出代码,对我这样的菜鸟还是有点难度的。说一点自己的理解:

start是标示位,表示从start下标开始一直到最后一个元素需要进行全排列。

当start等于最后一个元素下标时,全排列即为数组本身,所以将数组push到结果中;

若start不等最后一个元素下标,以start为循环初始值,一直到最后一个元素循环停止,循环体内执行的正是上面算法分析中的每一趟的内容:

将第i个元素放到第一个坑里,全排列剩下的元素(递归),然后恢复数组初始状态,i向前移,循环继续……

AC代码:
class Solution {
public:
    /*
     * @param nums: A list of integers.
     * @return: A list of permutations.
     */
    vector<vector<int>> permute(vector<int> &nums) {
        // write your code here
    vector<vector<int>> result;
    if (nums.empty())
    {
        result.push_back(nums); //此句不可少,否则输入[]时输出[],要求输出[[]]……;
        return result;
    }
    per(nums,0,result);
    return result;
    }
    
    
    void per(vector<int> &nums,int start,vector<vector<int>> &result)
{
    if (start==nums.size()-1)
    {
        result.push_back(nums);
    }

    for (int i=start;i<(int)nums.size();i++)
    {
        swap(nums[start],nums[i]);
        per(nums,start+1,result);
        swap(nums[start],nums[i]);
    }
}
};

递归算法的另一个版本:https://blog.csdn.net/this_is_qiqi/article/details/77844400

 

法2.非递归(参考 https://blog.csdn.net/aphysia/article/details/77774105

使用插入法,假如传进去的数字是1,2,3,那么先把1放进vector<int>中得到【1】,把【1】放到vector<vector<int>>(存放vector的vector)中得到【【1】】然后取出【【1】】里面的第一个数组【1】,往里面插入2,这个数字,有两种插法,就产生了两种排列【1,2】【2,1】,放进去得到【【1,2】,【2,1】】,然后取出【1,2】,插入3,有三种情况【3,1,2】,【1,3,2】,【1,2,3】,把它们放进去就是【【2,1】,【3,1,2】,【1,3,2】,【1,2,3】】,接着把【2,1】取出来,将3插进去,也有3中插法,就得到了最后的排列【【3,1,2】,【1,3,2】,【1,2,3】,【3,2,1】,【2,3,1】,【2,1,3】】,直到这里数组中的元素已经全部插入完毕。

AC代码:
class Solution {
public:
    /*
     * @param nums: A list of integers.
     * @return: A list of permutations.
     */
    vector<vector<int>> permute(vector<int> &nums) {
        // write your code here
        vector<vector<int>> result;
    if (nums.empty())
    {
        result.push_back(nums); //此句不可少,否则输入[]时输出[],要求输出[[]]……;
        return result;
    }

    vector<int> s;
    s.push_back(nums[0]);
    result.push_back(s); //第一个元素;

    for (int i=1;i<(int)nums.size();i++)
    {
        int size1=result.size();
        for (int j=0;j<size1;j++)
        {
            int size2=result[0].size();
            for (int k=0;k<=size2;k++) 
            {
                vector<int> temp=result[0];//每次都以第一个数组作为临时数组新增元素,增添完毕后要删除该数组;
                temp.insert(temp.begin()+k,nums[i]);
                result.push_back(temp); //结果数组更新是插在尾部,没有处理之前的临时数组,所以循环结束后应删掉第一个临时数组(临时数组使用完成);
            }
            result.erase(result.begin());
        }
    }

    return result;
    }
};

参考:vector中insert()的用法详解

补充:temp.insert(temp.begin()+k,nums[i]); —————— 在数组下标k处插入nums[i],insert新增元素不会覆盖原有元素。

 

 

法3.字典排序     此法能适应有重复元素的排列

要求初始数组是升序的。从最小字典序开始,一直到最大字典序。如果初始非升序(不是字典序最小值),将漏掉比初始数组小的字典序排列。

 

可参考:

全排列的实现方法--递归&字典序

【C++】全排列之非递归算法——字典序法(2)

lintcode 求全排列(15)

 AC代码:

class Solution {
public:
    /*
     * @param :  A list of integers
     * @return: A list of unique permutations
     */
    vector<vector<int>> permuteUnique(vector<int> &nums) {
        // write your code here
        vector<vector<int>> result;
    if (nums.empty())
    {
        result.push_back(nums); //此句不可少,否则输入[]时输出[],要求输出[[]]……;
        return result;
    }
    sort(nums.begin(),nums.end());
    result.push_back(nums);

    while(1)
    {
        int p,k;

        for (int i=nums.size()-1;i>=0;i--)
        {
            if (i<=0)//说明nums为字典序最大值;
            {
                return result;
            }
            if (nums[i-1]<nums[i])
            {
                p=i-1;
                break;
            }
        }
        for (int j=nums.size()-1;j>p;j--)
        {
            if (nums[j]>nums[p])
            {
                k=j;
                break;
            }
        }
        swap(nums[k],nums[p]);
        reverse(nums.begin()+p+1,nums.end());
        result.push_back(nums);
    }
    
    return result;
    }
};

 


posted @ 2018-04-24 18:32  eeeeeeee鹅  阅读(257)  评论(0编辑  收藏  举报