带重复元素的全排列

问题描述

给出一个具有重复数字的列表,找出列表所有不同的排列。


样例

给出列表 [1,2,2],不同的排列有:

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

递归解法:

利用回溯加递归的思想,每次将当前元素与待交换元素进行交换前,判断在当前元素到待交换元素之间是否存在元素与待交换元素相等。
若存在,则跳过待交换元素;若不存在,则进行交换,然后递归调用函数。
递归函数的出口是当待交换元素下标等于数组长度时,说明已经没有其他元素可以和当前元素交换了,递归结束。
当从递归函数返回时,再进行一次交换,也就是要进行回溯,将数组恢复原样。

class Solution {
public:
    /**
     * @param nums: A list of integers.
     * @return: A list of unique permutations.
     */
    vector<vector<int> > permuteUnique(vector<int> &nums) {
        
         vector<vector<int>> res;
        
        help(res, nums, 0);
         
        return res;
          
    }
  
    void help(vector<vector<int>> &res, vector<int>&num,int p){
        if (p ==num.size()){
            res.push_back(num);
        } else {
            for (int i = p; i<num.size(); i++) {
                if(contain(num,p,i))
                    continue;
                swap(num[i],num[p]);
                help(res, num, p+1);
                swap(num[i],num[p]);
            }                                                                           
        }
    }
    bool contain(vector<int>& num, int p, int i){
        if (i>p) {
            for (int j = p; j<i; j++){
                if (num[j]==num[i])
                    return true;
            }
        }
        return false;
    }  };


非递归解法:

非递归解法主要是求出全排列的下一个排序,当下一个排序与最初的排序相同时,说明所有排序已经求得,此时可以终止算法。
那么如何求得下一个排列呢?

我们通过观察原数组可以发现,如果从末尾往前看,数字逐渐变大,到了2时才减小的,然后我们再从后往前找第一个比2大的数字,是3,那么我们交换2和3,再把此时3后面的所有数字转置一下即可,步骤如下:

1  2  7  4  3  1

1  2  7  4  3  1

1  3  7  4  2  1

1  3  1  2  4  7

class Solution {
public:
    /**
     * @param nums: A list of integers.
     * @return: A list of unique permutations.
     */
    vector<vector<int> > permuteUnique(vector<int> &nums) {
        
         vector<vector<int>> res;
       
         vector<int> tmp = nums;
        
         do{
            res.push_back(nextPermutation(tmp));
         } while (tmp != nums);
         
        return res;
          
    }
    vector<int> nextPermutation(vector<int>& num){
        for(int i=num.size()-2; i>=0; i--){
            if (num[i] < num[i+1]){
                int j;
                for ( j=num.size()-1; j>i; j--){
                    if (num[j] > num[i])
                        break;
                }
                swap(num[j],num[i]);
                reverse(num.begin()+i+1, num.end());
                return num;
            }
        }
        reverse(num.begin(), num.end());
        return num;
    } };



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值