LeetCode题目总结——回溯

本文详细介绍了LeetCode中回溯算法的运用,涵盖多种组合问题,如电话号码的数字组合、括号生成、全排列等,并探讨了如何通过字典序法优化解题。此外,还讨论了如何利用回溯法寻找第k个元素,如第k个排列和组合。同时,深入剖析了深度优先搜索在单词搜索问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode题目总结——回溯

1. 找到所有组合

使用回溯法实现深度优先搜索,以找到满足某条件的所有结果并返回。

# cur: 当前正在尝试的组合
# ans: 当前已找到的满足条件的所有组合
# states: 用于判断条件是否满足的状态量
def backtrack(inputs, cur, ans, states):
	# 结束条件
	if states ...:
		ans.append(cur)
		return
		
	# 搜索当前节点下的每条路径
	for ...:
		cur.append(...)
		backtrack(inputs, cur, ans, states - ...)
		cur.pop()
  1. 0017 - 电话号码的数字组合
  2. 0022 - 括号生成
  3. 0039 - 组合总和
  4. 0040 - 组合总和 II
    使用一个数组记录元素剩余数量。
  5. 0046 - 全排列
    通过依次交换当前位置元素与其后方各个元素的位置,来完成回溯状态控制,不需要维护occupied数组。
  6. 0047 - 全排列 II
    必须维护occupied数组和cur数组,无法仅通过交换元素维护回溯状态。
  7. 0051 - N皇后
  8. 0052 - N皇后 II
    除了维护一个常规的occupied数组外,还要维护两个对角线occupied数组。同时可以考虑使用位运算加速,对角线occupied数组可以使用左移右移实现。
  9. 0090 - 子集II
    集合中存在重复元素,现对集合排序,再组合子集。重点注意如何跳过重复元素
for (int i = p; i < nums.size(); ++i) {
	// 重复元素跳过逻辑
	if (i > p && nums[i] == nums[i - 1]) {
		continue;
	}
	// 回溯逻辑
	cur.push_back(nums[i]);
	backtrack(res, nums, i + 1, cur);
	cur.pop_back();
}
  1. 0093 - 复原IP地址
  2. 0113 - 路径总和 II

2. 深度优先搜索

  1. 0079 - 单词搜索

3. 找到所有组合——字典序法

  1. 0077 - 组合
    字典序法,避免递归。
  2. 0078 - 子集
for (int mask = 0; mask < (1 << N); ++mask) {
	// 添加一条结果
	...
	for (int i = 0; i < N; ++i) {
		if (mask & (1 << i)) {
			// 添加结果中的一个元素
			...
		}
	}
}

4. 查找第k个元素

由于不需要所有的组合,因此可以通过运算快速定位第k个元素的位置,避免采用回溯从头开始遍历。

// 排列函数
size_t comb(int a, int b) {
	if (b == 0) return 1;
    size_t res = 1;
    for (int i = a - b + 1; i <= a; ++i) {
        res *= i;
    }
    for (int i = 1; i <= b; ++i) {
        res /= i;
    }
    return res;
}
// 组合函数
size_t perm(int a, int b) {
	if (b == 0) return 1;
    size_t res = 1;
    for (int i = a - b + 1; i <= a; ++i) {
        res *= i;
    }
    return res;
}
  1. 0060 - 第k个排列
    位置i的元素索引为:idx = [(k - 1) / (n - i)!]。 k的迭代公式为: k = (k - 1) % (n - i)! + 1
// 第k个排列函数
void perm_k(int a, int b, int k, vector<int>& res) {
    vector<int> occupied(a, false);
    // int div = perm(a, b);
    for (int p = 0; p < b; ++p) {
        // div /= a - p;
        // int s = (k - 1) / div;
        int s = (k - 1) / perm(a - p - 1, b - p - 1)
        for (int i = 0; i < occupied.size(); ++i) {
            if (!occupied[i]) {
                if (s == 0) {
                    res.emplace_back(i);
                    occupied[i] = true;
                    break;
                }
                --s;
            }
        }
        k = (k - 1) % div + 1;
    }
}
    
  1. 第k个组合
// 第k个组合函数
void comb_k(int a, int b, int k, vector<int>& res) {
	for (int p = 0; p < a; ++p) {
		if (res.size() == b) return;
		int t = comb(a - p - 1, b - res.size() - 1);
		if (k > t) {
			k -= t;
		}
		else {
			res.emplace_back(p);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值