zoukankan      html  css  js  c++  java
  • 代码题(19)— 组合与排列

    1、77. 组合

    给定两个整数 n 和 k,返回 1 ... 中所有可能的 k 个数的组合。

    示例:

    输入: n = 4, k = 2
    输出:
    [
      [2,4],
      [3,4],
      [2,3],
      [1,2],
      [1,3],
      [1,4],
    ]
    class Solution {
    public:
        vector<vector<int>> combine(int n, int k) {
            vector<vector<int>> res;
            vector<int> temp;
            combSum(n, k, 1, temp, res);
            return res;
        }
        void combSum(int n, int k, int pos, vector<int> &temp, vector<vector<int>> &res)
        {
            if(temp.size() == k)
            {
                res.push_back(temp);
                return;
            }
            for(int i=pos;i<=n;++i)
            {
                temp.push_back(i);
                combSum(n,k,i+1,temp,res);
                temp.pop_back();
            }
        }
             
    };

    2、从数组中取出n个元素的所有组合

    思路如下:

    首先我们找到这个字符组合中一定包含A字符的组合,那么A字符肯定就定下来了,即包含S中第一个字符的组合,然后以剩下的字符串BCDEF作为新的字符串,找出所有的2个字符的组合,那就得到了A包含的所有组合了是吧。。

    然后我们就可以省去A了,因为包含A的所有的组合都找到了,所以我们又开始以BCDEF这个字符串找出所有的3个字符的组合;

    那么做法和上面一样,又以第一个为指定的字符,CDEF进行找出所有2个字符的组合。这样包含B开头的所有字符组合也找完了吧‘

    依次遍历下去,达到E的时候,不能再进行了,因为剩余长度小于3个了。

      本质和上面是一样的/*我们有两种选择:第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;
    第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符*/

    void combineDfs(vector<int> &nums, int pos,int k, vector<int> &temp, vector<vector<int>> & res)
    {
        if (temp.size() == k)
        {
            res.push_back(temp);
        }
        else
        {
            for (int i = pos; i < nums.size(); ++i) //选择放还是不放
            {
                temp.push_back(nums[i]);
                combineDfs(nums, i + 1, k, temp, res);
                temp.pop_back();
            }
        }
    }
    
    vector<vector<int>> combine(vector<int> &nums, int k)
    {
        vector<int> temp;
        vector<vector<int>> res;
        combineDfs(nums, 0,k, temp, res);
        return res;
    
    }
    
    int main()
    {
        int n = 5, k = 3;
        vector<int> nums;
        //int a[] = { 2,4,6,8 };
        for (int i = 1; i <= n; ++i)
        {
            nums.push_back(i);
        }
        vector<vector<int>> res;
        res = combine(nums, k);
        for (int i = 0; i < res.size(); ++i)
        {
            for (int j = 0; j < res[i].size(); ++j)
            {
                cout << res[i][j] << "  ";
            }
            cout << endl;
        }
    
        system("pause");
        return 0;
    }

     3、46. 全排列

    给定一个没有重复数字的序列,返回其所有可能的全排列。

    示例:

    输入: [1,2,3]
    输出:
    [
      [1,2,3],
      [1,3,2],
      [2,1,3],
      [2,3,1],
      [3,1,2],
      [3,2,1]
    ]

    (1)这道题是求全排列问题,给的输入数组没有重复项,这跟之前的组合和类似,解法基本相同,但是不同点在于那道不同的数字顺序只算一种,是一道典型的组合题,而此题是求全排列问题,还是用递归DFS来求解。这里我们需要用到一个visited数组来标记某个数字是否访问过,然后在DFS递归函数从的循环应从头开始,而不是从level开始,这是和组合不同的地方,其余思路大体相同。

    class Solution {
    public:
        vector<vector<int>> permute(vector<int>& nums) {
            vector<vector<int>> res;
            vector<int> temp;
            vector<int> visited(nums.size(),0);
            permuteDfs(nums,0,visited,temp,res);
            return res;
            
        }
        void permuteDfs(vector<int> &nums, int pos, vector<int> &visited, vector<int> &temp, vector<vector<int>> &res)
        {
            if(pos == nums.size())
                res.push_back(temp);
            else
            {
                for(int i=0;i<nums.size();++i)
                {
                    if(visited[i] == 0)
                    {
                        visited[i] = 1;
                        temp.push_back(nums[i]);
                        permuteDfs(nums,pos+1,visited,temp,res);
                        temp.pop_back();
                        visited[i] = 0;
                    }
                }
            }
        }
    };

     (2)还有一种递归的写法,更简单一些,这里是每次交换num里面的两个数字,经过递归可以生成所有的排列情况。

    class Solution {
    public:
        vector<vector<int>> permute(vector<int>& nums) {
            vector<vector<int>> res;
            permuteDfs(nums, 0, res);
            return res;
            
        }
        void permuteDfs(vector<int> &nums, int pos, vector<vector<int>> &res)
        {
            if(pos == nums.size())
                res.push_back(nums);
            for(int i=pos;i<nums.size();++i)
            {
                swap(nums[pos], nums[i]);
                permuteDfs(nums, pos+1, res);
                swap(nums[pos], nums[i]);
            }
        }
        
    };

    4、47. 全排列 II

    还是DFS,不过有重复,那重点就是去重了。 
    同样深度的情况下,出现重复的,那么需要跳过。 具体说就是: 
    判断是否和上一个相等,相等的情况下如果上一个没用过,说明是上一个回溯结束的,同一层,那么就不要再重新来一轮了,跳过。 112 分别以1,1,2开始,第二个1,和第一个1开始的结果重复的。 
    在第一个1开始的时候,下一层的当前元素是第二个1,虽然也是1,但是上一个1被用了,说明它是不同深度的,所以不跳过。 
    这道题是之前那道 Permutations 全排列的延伸,由于输入数组有可能出现重复数字,如果按照之前的算法运算,会有重复排列产生,我们要避免重复的产生,在递归函数中要判断前面一个数和当前的数是否相等,如果相等,前面的数必须已经使用了,即对应的visited中的值为1,当前的数字才能使用,否则需要跳过,这样就不会产生重复排列了 

    class Solution {
    public:
        vector<vector<int>> permuteUnique(vector<int>& nums) {
            vector<vector<int>> res;
            vector<int> temp;
            vector<int> visited(nums.size(), 0);
            sort(nums.begin(), nums.end());
            permute(nums, 0, visited, temp, res);
            return res;
             
        }
        void permute(vector<int> &nums, int pos, vector<int> &visited, vector<int> &temp, vector<vector<int>> &res)
        {
            if(temp.size() == nums.size())
            {
                res.push_back(temp);
            }
            else
            {
                for(int i=0;i<nums.size();++i)
                {
                    if(visited[i] == 0)
                    {
                        if(i>0 && nums[i] == nums[i-1] && visited[i-1] == 0)
                            continue;
                        visited[i] = 1;
                        temp.push_back(nums[i]);
                        permute(nums, i+1, visited, temp, res);
                        temp.pop_back();
                        visited[i] = 0;
                    }
                }
            
            }
            
        }
    };

    还有一种比较简便的方法,在Permutations的基础上稍加修改,我们用set来保存结果,利用其不会有重复项的特点,然后我们再递归函数中的swap的地方,判断如果i和start不相同,但是nums[i]和nums[start]相同的情况下跳过,继续下一个循环.

    class Solution {
    public:
        vector<vector<int>> permuteUnique(vector<int>& nums) {
            set<vector<int>> res;
            permuteDfs(nums, 0, res);
            return vector<vector<int>> (res.begin(), res.end());
        }
    
        void permuteDfs(vector<int> &nums, int pos, set<vector<int>> &res)
        {
            if(pos == nums.size())
                res.insert(nums);
            for(int i=pos;i<nums.size();++i)
            {
                if (i != pos && nums[i] == nums[pos]) continue;
                swap(nums[pos], nums[i]);
                permuteDfs(nums, pos+1, res);
                swap(nums[pos], nums[i]);
            }
        }
    };
  • 相关阅读:
    Android app 简单的电话拨号器
    JavaWEB开发中的/到底代表什么
    springmvc json
    ForeignKey.on_delete
    django 实现指定文件合并成压缩文件下载
    常用SQL的优化
    数据库 两个简单实用的表级优化方法
    一天只能触发一次操作
    Ajax 生成流文件下载 以及复选框的实现
    django Q和F查询
  • 原文地址:https://www.cnblogs.com/eilearn/p/9291053.html
Copyright © 2011-2022 走看看