Leetcode 46/47/31/60 Permutations 專題

Leetcode 46 Permutations

Given a collection of distinct integers, return all possible permutations.
Example:

Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

求給定數組的所有排列,數組中不含有重複的數

method 1

寫法一:每次添加一個數,進行遞歸回溯,遞歸結束之後要刪去這個數,在同樣的位置換下一個數,每次添加之前要檢查這個數是否已經出現在序列中(來源於《算法競賽入門經典》)

void helper(vector<int>& nums, vector<vector<int>>& ans, vector<int> tmp){
	if (tmp.size() == nums.size()) ans.push_back(tmp);
	else{
		for (int i = 0; i < nums.size(); i++)
		{
			if (find(tmp.begin(), tmp.end(), nums[i]) != tmp.end()) continue;
			tmp.push_back(nums[i]);
			helper(nums, ans, tmp);
			tmp.pop_back();
		}
	}
}

vector<vector<int>> permute(vector<int>& nums) {
	vector<vector<int>> ans;
	if (nums.size() == 0) return ans;
	for (int i = 0; i < nums.size(); i++)
	{
		vector<int> tmp;
		tmp.push_back(nums[i]);
		helper(nums, ans, tmp);
	}

	return ans;
}

method 2

寫法二:每次遞歸,引入一個新的參數index,指示遍歷的開始,這樣就避免了檢查要添加的數是否已經出現過

void function(vector<vector<int>>& re, vector<int> temp, int index){
	if (index == temp.size())
		re.push_back(temp);
	for (int i = index; i<temp.size(); i++){
		swap(temp[index], temp[i]);			//不停地交換,得到新的序列
		function(re, temp, index + 1);
		swap(temp[index], temp[i]);
	}



}
vector<vector<int>> permute2(vector<int>& nums) {
	vector<vector<int>> re;
	if (nums.size() == 0)
		return re;
	function(re, nums, 0);
	return re;
}

Leetcode 47. Permutations II

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

Example:

Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
與I不同在於,數組中可能會出現重複的數字,這時算法有所不同

method 1

寫法一:在I的基礎上,每次添加數之前,首先要記錄tmp中該數出現的次數c1,再記錄數組中該數出現的次數c2,只有c1<c2時,才能添加該數,這樣纔不會重複。而且還要先對數組排序,因爲如果已經添加過第i個位置數num了,第i+1個位置上仍然是num,此時第i+1個位置的遞歸要省略,不然也會造成重複

void helper3(vector<int>& nums, vector<vector<int>>& ans, vector<int> tmp){
	if (tmp.size() == nums.size()) ans.push_back(tmp);
	else{
		for (int i = 0; i < nums.size(); i++)
		{
			if (!i || nums[i] != nums[i - 1]){
				int c1 = 0, c2 = 0;
				for (int j = 0; j < tmp.size(); j++) { if (nums[i] == tmp[j]) c1++; }
				for (int j = 0; j < nums.size(); j++) { if (nums[i] == nums[j]) c2++; }
				if (c1 < c2){
					tmp.push_back(nums[i]);
					helper3(nums, ans, tmp);
					tmp.pop_back();
				}
			}
		}
	}
}

vector<vector<int>> permuteUnique(vector<int>& nums) {
	sort(nums.begin(), nums.end());
	vector<vector<int>> ans;
	if (nums.size() == 0) return ans;
	vector<int> tmp;
	helper3(nums, ans, tmp);

	return ans;
}

method 2

寫法二:還是用交換的思想,引入index參數表示數組開始的位置

void recursion(vector<int> num, int index, vector<vector<int> > &res) {
	if (index == num.size() - 1) {
		res.push_back(num);
		return;
	}
	for (int k = index; k < num.size(); k++) {
		if (index != k && num[index] == num[k]) continue;    //排序的前提下,如果是相同的字符/已經加入過的字符,爲了避免重複,就不再加入了
		swap(num[index], num[k]);
		recursion(num, index + 1, res);
	}
}
vector<vector<int> > permuteUnique2(vector<int> &num) {
	sort(num.begin(), num.end());
	vector<vector<int> >res;
	recursion(num, 0, res);
	return res;
}

Leetcode 31. Next Permutation

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
eg:
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

給出已知一個排列,得到下一個比其大的排列

method 1

圖文解釋:
https://leetcode.com/problems/next-permutation/discuss/13994/Readable-code-without-confusing-ij-and-with-explanation
算法思想:

  1. 在從右往左看的遞增序列中找到第一個pivot
  2. 從右往左找第一個大於pivot的元素a
  3. 交換pivot和a
  4. 對pivot右邊的序列進行reverse
public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        while (i >= 0 && nums[i + 1] <= nums[i])
            i--;
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i + 1);
    }

    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }
    
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

Leetcode 60. Permutation Sequence

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, we get the following sequence for n = 3:

“123”
“132”
“213”
“231”
“312”
“321”
Given n and k, return the kth permutation sequence.

method 1

正常使用46題的思路,當選出第k個排列時,直接返回

void helper(vector<int>& nums, int& cur, int k, vector<int>& tmp){
	if (tmp.size() == nums.size()){
		cur++;
	}
	else{
		for (int i = 0; i < nums.size(); i++)
		{
			if (find(tmp.begin(), tmp.end(), nums[i]) != tmp.end()) continue; //這樣纔可以得到正確的順序,但是會超時
			tmp.push_back(nums[i]);
			helper(nums, cur, k, tmp);
			if (cur == k) return;
			tmp.pop_back();
		}
	}
}

string getPermutation(int n, int k) {
	vector<int> str;
	for (int i = 1; i <= n; i++)
		str.push_back(i);
	int cur = 0;
	vector<int> tmp;
	helper(str, cur, k, tmp);
	string vec;
	for (int i = 0; i < tmp.size(); i++)
		vec += ('0' + tmp[i]);
	return vec;
}

注意,這裏只有採用這個寫法,大小順序纔會對,如果採用swap+index的寫法,大小順序並不對

method 2

解釋:https://leetcode.com/problems/permutation-sequence/discuss/191348/c%2B%2B-0ms-100-with-algorithm-explanation
因爲是求第k個,所以可以根據大小關係直接求

string getPermutation2(int n, int k) {
	string res;
        string nums = "123456789";
        int f[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
        --k;
        for (int i = n; i >= 1; --i) {
            int j = k / f[i - 1];
            k %= f[i - 1];
            res.push_back(nums[j]);
            nums.erase(nums.begin() + j);
        }
        return res;
}

summary

  1. 如果是要求第k個,那麼往往可以根據大小關係,利用求餘操作求
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章