字典序全排列

題目

給定一個可包含重複數字的序列,返回所有不重複的全排列。

示例:

輸入: [1,1,2]
輸出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/permutations-ii

分析

在用幾個數字進行排列組合時,可以按照數字的大小順序進行排列。
如利用1 2 2 3 4進行排列組合。
1 2 2 3 4組成的數字最小的數爲12234,最大數字爲43221,字典序排列的關鍵在於從某一數字A查找下一個比這個數字A大的最小數字,關鍵步驟如下。

  1. 找到第一個大於前一個數字的位置 i
    從後往前找第一個順序的數字即nums[i]>nums[i-1]。如 13422 從2開始查找,4 2 2都是非順序(從左到右從小到大),直到掃描到3 4爲順序
  2. 找到大於nums[i-1]的最小的數所在的位置 j
    在nums[i]~nums[nums.size()-1]中找到最小的大於nums[i-1]的數,如4 2 2 中大於3的最小的數爲4記爲nums[j]
  3. 交換nums[i-1]和nums[j]
  4. 翻轉子數組nums[i…numsSize-1]
    由於每次選擇的是最小的大於nums[i-1]的元素與nums[i-1]交換,所以nums[i]~nums[nums.size()-1]是逆序(從大到小)排列的,將nums[i]到nums[nums.size()-1]進行reverse()就可以找到下一個數字

按照上述方法循環查找下一個數字,當達到最大值時,即所有數字逆序排列則停止尋找下一個數字。 如上例中,查找到43221時停止查找下一個數字。

代碼

class Solution {
public:
   
void swap(int &a, int &b) {
		int tmp = a;
		a = b;
		b = tmp;
	}
bool nextNum(vector<int>& nums) {
		for (int i = nums.size() - 1;i > 0;i--) {
			/*找到第一個大於前一個數字的位置i*/
			if (nums[i] > nums[i - 1]) { 
				/*找到大於nums[i-1]的最小的數所在的位置j*/
				for (int j = nums.size() - 1;j >= i;j--) {
					if (nums[j] > nums[i - 1]) {
						/*交換nums[i-1]和nums[j]*/
						swap(nums[j], nums[i - 1]);
						break;
					}
				}
				/*翻轉子數組nums[i...numsSize-1]*/
				reverse(nums.begin()+i,nums.end());		
				return true;
			}
		}
		/*最大的數沒有下一個排列數 如4321*/
		return false;
	}
	vector<vector<int>> permuteUnique(vector<int>& nums) {
		vector<vector<int>> res;
		//vector<int> tmp;
		bool flag = true;
		
		sort(nums.begin(), nums.end());
		while (flag) {
            res.push_back(nums);
			flag = nextNum(nums);
		}
		return res;
	}
};

提交結果

在這裏插入圖片描述

積累

對vector、set等容器進行排序時,使用sort(nums.begin(),nums.end(),cmp);而不用nums.sort(),且在用時要incude,cmp爲比較規則。
可以使用reverse(nums.begin()+cnt,nums.end())進行數組局部逆轉。

發佈了38 篇原創文章 · 獲贊 13 · 訪問量 4025
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章