组合型枚举之回溯法与字典序法的C++实现

218 阅读1分钟

leetcode 77. 组合

递归型(回溯法)

class Solution {
public:
    vector<vector<int>> ans;
    int n;
    int k;
    void dfs(int pre,vector<int> v)
    {
        if(v.size()==k)
        {
            ans.push_back(v);
            return;
        }
        if(pre>n)return;
        for(int i=pre+1;i<=n;i++)
        {
            v.push_back(i);
            dfs(i,v);
            v.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        this->n=n;
        this->k=k;
        dfs(0,{});
        return ans;
    }
};
  • 时间复杂度:O(C(n,k)*k)
  • 空间复杂度:O(n+k),即递归使用栈空间的空间代价和临时数组v的空间代价。

非递归型(字典序法)

class Solution {
public:
	string& nextone(string& s)
	{
		// 长度为n的s,其中包含k个'1'与n-k个'0'
        // 按照字典序,求当前s的下一个s
        // 例如
		// 00111 -> 01011 -> 01101 -> 01110 -> 10011
		// -> 10101 -> 10110 -> 11001 -> 11010 -> 11100
        // 取最右边但不是末尾的0
		// 右边的1与紧邻左边的0作swap
		// 如果这个0不是最右边的0
		// 被swap的1后面的1都移至最右边
		int rht = s.size() - 1;
		bool isend = true;
		while (rht >= 0 && s[rht] == '0')
		{
			isend = false;
			rht--;
		}
		if (rht < 0)
		{
			// 00000
			s = "";
			return s;
		}
		int lft = rht;
		while (lft >= 0 && s[lft] == '1')
		{
			lft--;
		}
		if (lft < 0)
		{
			// 11100, the last one
			s = "";
			return s;
		}
		if (lft >= 0 && s[lft] == '0')
		{
			swap(s[lft], s[lft + 1]);
		}
		if (!isend)
		{
			rht = s.size() - 1;
			lft = lft + 2;
			while (lft < rht && s[lft] == '1' && s[rht] == '0')
			{
				s[lft++] = '0';
				s[rht--] = '1';
			}
		}
		return s;
	}
	vector<vector<int>> combine(int n, int k) {
		string s = string(n-k, '0') + string(k, '1');
		vector<vector<int>> ans;
		while (s != "")
		{
			vector<int> v;
			int i = 0;
			while (i++ < n)
			{
				if (s[n-i] == '1')v.push_back(i);
			}
			ans.push_back(v);
			nextone(s);
		}
		return ans;
	}
};

  • 时间复杂度:O(C(n,k)*k),要求C(n,k)nextone,每次nextonek个'1'的位置进行调整,循环操作从k到n次
  • 空间复杂度:O(k),即临时数组v的空间代价。
int main()
{
	Solution sol;
	int n = 5, k = 3;
	string s = string(n-k, '0') + string(k, '1');
	int t = 0;
	while (s != "")
	{
		cout << ++t << ": \t" << s << endl;
		sol.nextone(s);
	}
	cout << "---" << t << "---" << endl;
	vector<vector<int>> ans = sol.combine(n, k);
	t=0;
	for (auto v : ans)
	{
		cout << ++t << ": \t";
		for (auto a : v)cout << a << " ";
		cout << endl;
	}
}

输出

1:      00111
2:      01011
3:      01101
4:      01110
5:      10011
6:      10101
7:      10110
8:      11001
9:      11010
10:     11100
---10---
1:      1 2 3
2:      1 2 4
3:      1 3 4
4:      2 3 4
5:      1 2 5
6:      1 3 5
7:      2 3 5
8:      1 4 5
9:      2 4 5
10:     3 4 5