zoukankan      html  css  js  c++  java
  • 【LeetCode】Permutation全排列

    1. Next Permutation

        实现C++的std::next_permutation函数,重新排列范围内的元素,返回按照 字典序 排列的下一个值较大的组合。若其已经是最大排列,则返回最小排列,即按升序重新排列元素。不能分配额外的内存空间。

    void nextPermutation(vector<int>& nums) {
        next_permutation(nums.begin(), nums.end());
    }

        全排列 Permutation 问题已经被古人研究透了,参见 Wikipedia page,Next Permutation 有一个经典的简单有效算法,还能解决含有重复元素的全排列问题。

    1. 从尾端寻找第一个下标 i,使 nums[i] < nums[i + 1]。若这样的 i 不存在,则这个排列已经是降序排列(或者元素全部相同),那么直接逆序得到升序排列。
    2. 从尾端寻找第一个下标 j,使 nums[i] < nums[j]。若 i 存在,则 j 一定存在且 i < j,因为 j 起码可以 = i + 1。
    3. 交换 i 和 j 位置的元素。
    4. 逆序从 i + 1 到结尾的子数组,即求出下一个序列。

        e.g. 

    nums = [6, 3, 4, 9, 8, 7, 1]
                  i        j
    1. 找到 i = 2,nums[i] = 4。i 右边的一定是降序排列 [9, 8, 7, 1]
    2. 从尾端找第一个大于 nums[i] 的数 7,即 nums[j] = 7。
    3. 交换 4 和 7,[6, 3, 7, 9, 8, 4, 1]。保证交换后的 [6, 3, 7, ......] 一定大于原来的 [6, 3, 4, ......],且可以发现 i 右边的 [9, 8, 4, 1] 仍然是降序排列
    4. 对 i 右边进行逆序,得到结果 [6, 3, 7, 1, 4, 8, 9]。

    C++实现: 

     1 void nextPermutation(vector<int>& nums) {
     2     int j = nums.size() - 1, i = j;
     3     while (--i >= 0) {
     4         if (nums[i] < nums[i + 1])
     5             break;
     6     }
     7     if (i != -1) {
     8         while (j > i) {
     9             if (nums[j] > nums[i])
    10                 break;
    11             j--;
    12         }
    13         swap(nums[i], nums[j]);
    14     }
    15     reverse(nums.begin() + i + 1, nums.end());
    16     return;
    17 }

    2. Permutations

        给出一组不含重复数字的数组的全排列。

    e.g. [1,2,3] 有全排列:

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

    我使用递归实现:

     1 vector<vector<int>> permute(vector<int>& nums) {
     2     vector<vector<int>> result;
     3     vector<int> item;
     4     add(result, nums, item);
     5     return result;
     6 }
     7     
     8 void add(vector<vector<int>> &result, vector<int> v, vector<int> item) {
     9     if (v.empty()) {
    10         result.push_back(item);
    11         return;
    12     }
    13     for (int i = 0; i < v.size(); i++) {
    14         item.push_back(v[i]);
    15         vector<int> new_v;
    16         for (int j = 0; j < v.size(); j++) {
    17             if (j == i) continue;
    18             new_v.push_back(v[j]);
    19         }
    20         add(result, new_v, item);
    21         item.pop_back();
    22     }
    23 }

    看到答案一种更好的方法,使用 swap 函数,不需要创建额外的 item 存储大量重复的数据。

    如果 permuteRecursive 函数的 num 参数不用引用,则可以去掉第 15 行的 swap,但这样会创建大量的临时 vector,效率低不少。

    而且个人觉得这个方法有点难以理解。真是天才!

     1 vector<vector<int> > permute(vector<int> &num) {
     2     vector<vector<int>> result;
     3     permuteRecursive(result, num, 0);
     4     return result;
     5 }
     6 
     7 void permuteRecursive(vector<vector<int>> &result, vector<int> &num, int begin) {
     8     if (begin == num.size()) {
     9         result.push_back(num);
    10         return;
    11     }
    12     for (int i = begin; i < num.size(); i++) {
    13         swap(num[begin], num[i]);
    14         permuteRecursive(result, num, begin + 1);
    15         swap(num[begin], num[i]);
    16     }
    17 }

    3. Permutations II

        给出一组可能含有重复数字的数组的全排列。

    e.g. [1,1,2] 有全排列:

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

    我使用 (1) Next Permutation 的思路,从升序开始逐个计算下一个全排列。

     1 vector<vector<int>> permuteUnique(vector<int>& nums) {
     2     vector<vector<int>> result;
     3     sort(nums.begin(), nums.end());
     4     do {
     5         result.push_back(nums);
     6     } while (nextPermutation(nums));
     7     return result;
     8 }
     9     
    10 bool nextPermutation(vector<int>& nums) {
    11     int i = nums.size() - 1;
    12     while (--i >= 0 && nums[i] >= nums[i + 1]);
    13     if (i != -1) {
    14         int j = nums.size();
    15         while (--j > i && nums[j] <= nums[i]);
    16         swap(nums[i], nums[j]);
    17         reverse(nums.begin() + i + 1, nums.end());
    18         return true;
    19     }
    20     return false;
    21 }

    别人写了一种类似 (2) 中递归的方法,也比较难以理解。这种情况下不能用引用,以及在递归语句后把交换元素再换回来。

     1 vector<vector<int> > permuteUnique(vector<int> &num) {
     2     sort(num.begin(), num.end());
     3     vector<vector<int>> result;
     4     permuteRecursive(result, num, 0);
     5     return result;
     6 }
     7 
     8 void permuteRecursive(vector<vector<int>> &result, vector<int> num, int begin) {
     9     if (begin == num.size()) {
    10         result.push_back(num);
    11         return;
    12     }
    13     for (int i = begin; i < num.size(); i++) {
    14         if (i > begin && num[i] == num[begin])  continue;
    15         swap(num[begin], num[i]);
    16         permuteRecursive(result, num, begin + 1);
    17     }
    18 }
  • 相关阅读:
    Qt-网易云音乐界面实现-4 实现推荐列表和我的音乐列表,重要在QListWidget美化
    Qt-网易云音乐界面实现-3 音乐名片模块的实现
    Qt-网易云音乐界面实现-2 红红的程序运行图标,和相似下方音乐条
    Qt-网易云音乐界面实现-1 窗口隐藏拖拽移动,自定义标题栏
    Qt 利用XML文档,写一个程序集合 四
    promise的简单理解
    toast弹框组件的封装-插件方式
    vuex自动获取当前的时间
    用vue对tabbar的封装
    子组件与父组件的各种传递关系
  • 原文地址:https://www.cnblogs.com/wayne793377164/p/7161786.html
Copyright © 2011-2022 走看看