▶ 问题:字典序生成有关的问题。
▶ 31. 由当前序列生成字典序里的下一个序列。
● 初版代码,19 ms
1 class Solution 2 { 3 public: 4 void nextPermutation(vector<int>& nums) 5 { 6 int i, j, temp; 7 for (i = nums.size() - 1; i > 0 && nums[i - 1] >= nums[i]; i--);// 从右向左寻找第一个递增对 8 if (!i) // i == 0,当前排序已经是最大的,全反序得到最小的 9 { 10 for (j = 0; i < nums.size() / 2; j++) 11 temp = nums[j], nums[j] = nums[nums.size() - 1 - j], nums[nums.size() - 1 - j] = temp; 12 return; 13 } 14 else if (i == nums.size() - 1) // 最后两个元素就是递增对,交换他俩就行 15 { 16 temp = nums[i], nums[i] = nums[i - 1], nums[i - 1] = temp; 17 return; 18 } 19 for (j = nums.size() - 1; j >= i && nums[j] <= nums[i - 1]; j--);// 其他情形,需要在右侧递减部分中找到比递增较小元更大的第一个元素 20 temp = nums[i - 1], nums[i - 1] = nums[j], nums[j] = temp; // 将右侧较大的元素交换到递增对较小元的位置 21 for (j = i; j < (nums.size() + i) / 2; j++) // 右侧进行反序 22 temp = nums[j],nums[j] = nums[nums.size() + i - 1 - j],nums[nums.size() + i - 1 - j] = temp; 23 return; 24 } 25 };
● 改进代码,15 ms,第二次查找时使用二分法改进,引入原数组长度的常量,引入内置交换函数 std::swap()。
1 class Solution 2 { 3 public: 4 void nextPermutation(vector<int>& nums) 5 { 6 const int len = nums.size(); 7 int i, j, temp, lp; 8 for (i = len - 1; i > 0 && nums[i - 1] >= nums[i]; i--); 9 if (!i) 10 { 11 for (i = 0; i < len / 2; i++) 12 swap(nums[i], nums[len - 1 - i]); 13 return; 14 } 15 else if (i == len - 1) 16 { 17 swap(nums[i], nums[i - 1]); 18 return; 19 } 20 for (lp = i, j = len, temp = (lp + j) / 2; temp > lp; temp = (lp + j) / 2) 21 (nums[temp] > nums[i - 1]) ? (lp = temp) : (j = temp); 22 swap(nums[i - 1], nums[lp]); 23 for (j = i; j < (len + i) / 2; j++) 24 swap(nums[j], nums[len + i - 1 - j]); 25 return; 26 } 27 };
● 大佬的代码,13 ms,使用了排序函数
1 class Solution 2 { 3 public: 4 void nextPermutation(vector<int>& nums) 5 { 6 const int len = nums.size(); 7 int i, flag, index; 8 for (flag = 1, i = index = len - 1; i > 0; i--) 9 { 10 if (nums[i] <= nums[i - 1]) 11 index--; 12 else 13 { 14 for (int j = len - 1; j >= index; j--) 15 { 16 if (nums[j] > nums[i - 1]) 17 { 18 swap(nums[i - 1], nums[j]); 19 break; 20 } 21 } 22 sort(nums.begin() + i, nums.end()); 23 flag = 0; 24 break; 25 } 26 } 27 if (len && flag) 28 sort(nums.begin(), nums.end()); 29 } 30 };
● 偷懒的代码,14 ms,直接使用内置函数 next_permutation(),该函数返回值是一个 bool 变量,仅当输入的数组引用是一个降序时返回 false,但此时仍然将数组调整为字典序的下一个(升序)。
1 class Solution 2 { 3 public: 4 void nextPermutation(vector<int>& nums) 5 { 6 next_permutation(nums.begin(), nums.end()); 7 return; 8 } 9 };
▶ 46. 生成给定元素的所有排列(元素无重复)
● 自己的代码,13 ms,反复调用第 31 题的结果来进行枚举。
1 class Solution 2 { 3 public: 4 void nextPermutation(vector<int>& nums) 5 { 6 const int len = nums.size(); 7 int i, j, temp, lp; 8 for (i = len - 1; i > 0 && nums[i - 1] >= nums[i]; i--); 9 if (!i) 10 { 11 for (i = 0; i < len / 2; i++) 12 swap(nums[i], nums[len - 1 - i]); 13 return; 14 } 15 else if (i == len - 1) 16 { 17 swap(nums[i], nums[i - 1]); 18 return; 19 } 20 for (lp = i, j = len, temp = (lp + j) / 2; temp > lp; temp = (lp + j) / 2) 21 (nums[temp] > nums[i - 1]) ? (lp = temp) : (j = temp); 22 swap(nums[i - 1], nums[lp]); 23 for (j = i; j < (len + i) / 2; j++) 24 swap(nums[j], nums[len + i - 1 - j]); 25 return; 26 } 27 vector<vector<int>> permute(vector<int>& nums) 28 { 29 int count, i; 30 vector<vector<int>> output; 31 for (i = nums.size(), count = 1; i > 1; count *= i, i--); // 计算阶乘(总共排列数) 32 sort(nums.begin(),nums.end()); // 排序得到第一个排列 33 output.push_back(nums); 34 for (i = 1; i < count; i++) // 每次循环生成下一个排列,不合并压入可以优化最后一次函数 nextPermutation()调用 35 { 36 nextPermutation(nums); 37 output.push_back(nums); 38 } 39 return output; 40 } 41 };
● 大佬的代码,13 ms,使用了内置函数 next_permutation()
1 class Solution 2 { 3 public: 4 vector<vector<int>> permute(vector<int>& nums) 5 { 6 vector<vector<int>> result; 7 sort(nums.begin(), nums.end()); 8 for (result.push_back(nums); next_permutation(nums.begin(), nums.end());) 9 result.push_back(nums); 10 return result; 11 } 12 };
▶ 47. 生成给定元素的所有排列(元素有重复)
● 自己的代码,26 ms,原数组排序后统计各重复部分的长度,计算重复排列数。由于前面 31 题中的程序在求“下一个排列”的时候已经跳过了重复的情形,所以只要恰当的减少该函数的调用次数就能列出所有含重复元素的排列。
1 class Solution 2 { 3 public: 4 void nextPermutation(vector<int>& nums)// 改进 5 { 6 const int len = nums.size(); 7 int i, j, temp, lp; 8 for (i = len - 1; i > 0 && nums[i - 1] >= nums[i]; i--); 9 if (!i) 10 { 11 for (i = 0; i < len / 2; i++) 12 swap(nums[i], nums[len - 1 - i]); 13 return; 14 } 15 else if (i == len - 1) 16 { 17 swap(nums[i], nums[i - 1]); 18 return; 19 } 20 for (lp = i, j = len, temp = (lp + j) / 2; temp > lp; temp = (lp + j) / 2) 21 (nums[temp] > nums[i - 1]) ? (lp = temp) : (j = temp); 22 swap(nums[i - 1], nums[lp]); 23 for (j = i; j < (len + i) / 2; j++) 24 swap(nums[j], nums[len + i - 1 - j]); 25 return; 26 } 27 inline int factorial(int a)// 阶乘函数 28 { 29 int i, output; 30 for (output = 1, i = 2; i <= a; output *= i, i++); 31 return output; 32 } 33 vector<vector<int>> permuteUnique(vector<int>& nums) 34 { 35 int count, i, rep; 36 vector<vector<int>> output; 37 count = factorial(nums.size()); // 计算重复排列数 38 sort(nums.begin(), nums.end()); // 排序得到第一个排列 39 for (i = 0; i < nums.size() - 1; i++) 40 { 41 for (; i < nums.size() - 1 && nums[i] != nums[i + 1]; i++); // 跳过相邻项不同的部分 42 for (rep = 0; i < nums.size() - 1 && nums[i] == nums[i + 1]; rep++, i++); // 记录本节中相邻项相同的个数 43 count /= factorial(rep + 1); // 重复排列的计算 44 } 45 output.push_back(nums); 46 for (i = 1; i < count; i++) // 每次循环生成下一个排列,不合并压入可以优化最后一次函数 nextPermutation()调用 47 { 48 nextPermutation(nums); 49 output.push_back(nums); 50 } 51 return output; 52 } 53 };
● 大佬的代码,30 ms,回溯,逐个寻找新排列。
1 class Solution 2 { 3 public: 4 vector<vector<int>> permuteUnique(vector<int>& nums) 5 { 6 vector<vector<int>> result; // 存放结果 7 vector<int> flags(nums.size(), 0), content;// flag[i] 表当前 nums[i] 是否被使用,content 为当前排列(填满就送入 reslut) 8 sort(nums.begin(), nums.end()); 9 helper(nums, content, flags, result); 10 return result; 11 } 12 13 void helper(vector<int>& nums, vector<int>& content, vector<int>& flags, vector<vector<int>>& result) 14 { 15 if (content.size() == nums.size())// 所有元素都被使用,将 content 中的排列放入 result 中 16 { 17 result.push_back(content); 18 return; 19 } 20 for (int i = 0; i < nums.size(); i++) 21 { 22 if (i > 0 && nums[i] == nums[i - 1] && flags[i - 1] != 0)// 跳过相同的元素 23 break; 24 if (!flags[i])// 元素 nums[i] 还没有被谁用过 25 { 26 flags[i] = 1; // 把元素 nums[i] 添加到当前的排列末尾 27 content.push_back(nums[i]); 28 int i1 = i; 29 while (i < nums.size() - 1 && nums[i + 1] == nums[i])// 跳过相同元素 30 i++; 31 helper(nums, content, flags, result); // 尝试在添加了元素 nums[i] 以后继续添加下一个元素 32 content.pop_back(); // 去掉当前添加的元素,由于元素可能重复,所以需要在最后一个元素处标记未使用 33 flags[i1] = 0; 34 } 35 } 36 } 37 };
● 大佬的代码改进,27 ms,基本算法相同。
1 class Solution 2 { 3 public: 4 vector<vector<int>> permuteUnique(vector<int>& nums) 5 { 6 vector<vector<int>> result; 7 vector<bool> flags(nums.size(), true); 8 vector<int> content; 9 sort(nums.begin(), nums.end()); 10 helper(nums, content, flags, result); 11 return result; 12 } 13 void helper(vector<int>& nums, vector<int>& content, vector<bool>& flags, vector<vector<int>>& result) 14 { 15 if (content.size() == nums.size()) 16 { 17 result.push_back(content); 18 return; 19 } 20 int i, j; 21 for (i = 0; i < nums.size(); i++) 22 { 23 if (flags[i]) 24 { 25 flags[i] = false; 26 content.push_back(nums[i]); 27 for (j = i; i < nums.size() - 1 && nums[i + 1] == nums[i]; i++); 28 helper(nums, content, flags, result); 29 content.pop_back(); 30 flags[j] = true; 31 } 32 } 33 } 34 };
▶ 60. 求给定元素字典序排列中的第 k 个。
● 初版,108 ms,逐个生成排列,直到第 k 个为止。
1 class Solution 2 { 3 public: 4 string getPermutation(int n, int k) 5 { 6 int i; 7 vector<int> table(n); 8 for (i = 0; i < n; table[i] = i++); 9 for (i = 1; i < k; i++) 10 next_permutation(table.begin(), table.end()); 11 string output(""); 12 for (i = 0; i < n; i++) 13 output += table[i] + '1'; 14 return output; 15 } 16 };
● 改进版,6 ms,利用阶乘计算出所求排列,最快的解法算法与之相同。
1 class Solution 2 { 3 public: 4 string getPermutation(int n, int k) 5 { 6 int i, j; 7 string ret; 8 vector<int> factorial(n, 1);// 阶乘和字符的打表 9 vector<char> num(n, 1); 10 11 for (i = 1; i < n; i++) 12 factorial[i] = factorial[i - 1] * i; 13 for (i = 0; i < n; i++) 14 num[i] = '0' + (i + 1); 15 for (i = n, k--; i >= 1; i--) 16 { 17 j = k / factorial[i - 1]; 18 k %= factorial[i - 1]; 19 ret.push_back(num[j]); 20 num.erase(num.begin() + j); 21 } 22 return ret; 23 } 24 };