代码题(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]);
        }
    }
};
原文地址:https://www.cnblogs.com/eilearn/p/9291053.html