LeetCode - 排列组合类题目总结

电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

在这里插入图片描述
示例:输入:“23”;输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
分析:回溯法

class Solution {
//总结字符串的全排列问题,用递归解决不同输入情形下嵌套for循环的个数的问题。
public:
    vector<string> letterCombinations(string digits) {
        unordered_map<char, string> table{
            {'0', " "}, {'1',"*"}, {'2', "abc"},
            {'3',"def"}, {'4',"ghi"}, {'5',"jkl"},
            {'6',"mno"}, {'7',"pqrs"},{'8',"tuv"},
            {'9',"wxyz"}};  
        vector<string> res;
        if(digits == "") return res;
        func(res, "", digits, table, 0);
        return res;
    }
    
    void func(vector<string> &res, string str, string &digits, unordered_map<char, string> &m, int k){
        if(str.size() == digits.size()){
            res.push_back(str);
            return;
        }
        string tmp = m[digits[k]];
        for(char w : tmp){
            str += w;
            func(res, str, digits, m, k+1);
            str.pop_back();
        }
        return ;
    }
};

全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。
示例: 输入: [1,2,3],
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
分析:回溯递归

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

全排列II

给定一个可包含重复数字的序列返回所有不重复的全排列
示例: 输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

分析:回溯递归 ,用map去重,map的key唯一不重复,map统计每个字符出现的次数。每个位置为不重复的字符轮询一遍,若某个字符在之前已经出现完,则后续不再轮询该字符(用map的second值统计次数)。

//每个位置的字符 为不重复的字符轮询一遍。
class Solution {
public:
    void backtrace(map<int, int>& m, int k, int n,
                   vector<int>& v, vector<vector<int> >& res) {
        if (k == n) {
            res.push_back(v);
            return;
        }
        for (auto& p : m) {
            if (p.second == 0) continue; //该字符已经在之前轮询完毕
            --p.second;
            v.push_back(p.first);
            backtrace(m, k + 1, n, v, res);
            ++p.second;
            v.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        map<int, int> m;
        for (auto x : nums) ++m[x];
        vector<vector<int> > res;
        vector<int> v;
        backtrace(m, 0, nums.size(), v, res);
        return res;
    }
};

组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:所有数字(包括 target)都是正整数。解集不能包含重复的组合。
示例 :输入: candidates = [2,3,6,7], target = 7,所求解集为:
[
[7],
[2,2,3]
]
分析: DFS深度优先搜索

class Solution {
public:
	vector<int> tmp;
	vector<vector<int>> ans;

	void dfs(int now, int sum, int target, vector<int>& num)
	{
		if (sum > target) 
			return;
		if (sum == target)
		{
			ans.push_back(tmp);
			return;
		}
//元素的第一个数依次从canditates的下标0~size()-1依次遍历,now其实为上一个所push节点的下标
		for (int i = now; i < num.size(); i++) 
		{
			tmp.push_back(num[i]);
			dfs(i, sum + num[i], target, num);
			tmp.pop_back();
		}
	}

	vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
		dfs(0, 0, target, candidates);
		return ans;
	}
};

组合总和II

给定一个输入数字可重复的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
分析:用map的first把输入变成不重复的输入数组,用map的second变为不重复输入数组中每个值可重复作为解的次数。此题即可转换为组合总和题中的算法求解。

class Solution {
public:
	vector<int> tmp;
	vector<vector<int>> ans;

	void dfs(map<int,int>::iterator now, int sum, int target, map<int,int>&mnum)
	{
		if (sum > target) 
			return;
		if (sum == target)
		{
			ans.push_back(tmp);
			return;
		}
//元素的第一个数依次从canditates的下标0~size()-1依次遍历,now其实为上一个所push节点的下标
		for (auto i = now; i!= mnum.end(); i++) 
		{
			if (i->second == 0)
				continue;
			--(i->second);
			tmp.push_back(i->first);
			dfs(i, sum + i->first, target, mnum);
			++(i->second);
			tmp.pop_back();
		}
	}

	vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
		map<int, int>mnum;
		for (int value : candidates)
			++mnum[value];

		dfs(mnum.begin(), 0, target, mnum);
		return ans;
	}
};

组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 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> tmp;
		if (n <= 0||k>n)
			return res;
		if (n == k)
		{
			for (int i = 1; i <= n; i++)
				tmp.push_back(i);	
			res.push_back(tmp);
			return  res;
		}
		dfs(n, k,1, res, tmp);
		return res;
	}
	void dfs(int n, int k, int now, vector<vector<int>>&res, vector<int> tmp) {
		if (k == tmp.size())
		{
			res.push_back(tmp);
			return;
		}
		for (int i = now; i <= n; i++) {
			tmp.push_back(i);
			dfs(n, k, i + 1, res, tmp);
			tmp.pop_back();
		}
	}

};

子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

示例: 输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
分析:回溯递归

class Solution {
public:
	vector<vector<int>> subsets(vector<int>& nums) {
		vector<vector<int>>res;
		for (int i = 0; i <=nums.size(); i++)
		{
			vector<int> tmp;
			dfs(nums, 0, i, res, tmp);
		}
		return res;
	}
	void dfs(vector<int>& nums,int now, int k, vector<vector<int>>&res, vector<int> tmp) {
		
		if (tmp.size() == k)
		{
			res.push_back(tmp);
			return;
		}
		if ((nums.size() - now  + tmp.size() < k)|| (now >= nums.size()))
			return;
		for (int i = now; i <nums.size() ; i++) {
			tmp.push_back(nums[i]);
			dfs(nums, i+1,k, res, tmp);
			tmp.pop_back();
		}
	}
};

子集II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
示例:输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
分析:用map的first把输入变成不重复的输入数组,用map的second变为不重复输入数组中每个值可重复作为解的次数。此题即可转换为子集题中的算法求解。

class Solution {
public:
	vector<vector<int>> subsetsWithDup(vector<int>& nums) {
		vector<vector<int>>res;
		map<int, int>mnum;
		for (int i : nums)
			++mnum[i];
		for (int i = 0; i <=nums.size(); i++)
		{
			vector<int> tmp;
			dfs(mnum,mnum.begin(), i, res, tmp);
		}
		return res;
	}
	void dfs(map<int,int>&nums,map<int,int>::iterator now, int k, vector<vector<int>>&res, vector<int> tmp) {
		
		if (tmp.size() == k)
		{
			res.push_back(tmp);
			return;
		}
		for (auto i = now; i !=nums.end() ; i++) {
			if (i->second == 0)
				continue;
			tmp.push_back(i->first);
			(i->second)--;
			dfs(nums, i,k, res, tmp);
			tmp.pop_back();
			(i->second)++;
		}
	}
};

发布了76 篇原创文章 · 获赞 6 · 访问量 2778

猜你喜欢

转载自blog.csdn.net/u014618114/article/details/104130975