• 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;
        }
    };

  • 相关阅读:
    通过实验窥探javascript的解析执行顺序
    HTML5实战与剖析之原生拖拽(四可拖动dragable属性和其他成员)
    Google Guava之Optional优雅的使用null
    sharding-jdbc源码学习(一)简介
    重构——改善既有代码的设计
    spring自定义标签
    java自定义注解
    开源项目集合
    Lombok引入简化Java代码
    设计模式之建造者模式
  • 原文地址:https://www.cnblogs.com/Tang-tangt/p/8932710.html
Copyright © 2020-2023  润新知