LeetCodeHot100|回溯算法、46.全排列、78.子集、17.电话号码的字母组合

回溯算法

回溯算法是一种通过穷举来解决问题的方法,它的核心是从一个初始状态出发,暴力搜索所有可能的解决方案,当遇到正确的解则将其记录,直到找到解或者尝试了所有可能的选择都无法找到解为止。

  • 回溯中回退
  • 回溯中的减枝
  • 常见代码框架

回溯中回退

给定一棵二叉树,搜索并记录所有值为 的节点,请返回节点列表。

cpp 复制代码
void preOrder(TreeNode *root) { 
	if(root == nullptr) { 
		return;
	}
	// 尝试
	path.push_back(root);
	if(root ->val == 7) { 
		//记录解
		res.push_back(path);
	}
	preOrder(root -> left);
	preOrder(root -> right);
	// 回退
	path.pop_back();
}
}

回溯中的减枝

在二叉树中搜索所有值为 7 的节点,请返回根节点到这些节点的路径,并要求路径中不包含值为 3 的节点

cpp 复制代码
void preOrder(TreeNode *root) { 
	// 减枝
	if(root == nullptr || root->val == 3) { 
		return;
	}
	// 尝试
	path.push_back(root);
	if(root->val == 7) {
		res.push_back(path);
	}
	preOrder(root -> left);
	preOrder(root -> right);
	// 回退
	path.pop_back();
}

常见代码框架

46.全排列

给定一个不含重复数字的数组nums,返回其所有可能的全排列,你可以按任意顺序返回答案。

思路分析

根据数据排列的特点,考虑深度优先搜索所有排列方案,就是通过元素交换,现固定第一位元素(n种情况)、再固定第2位元素(n - 1)种情况,最后固定第n位元素。

cpp 复制代码
class Solution {

public:

    vector<vector<int>> permute(vector<int>& nums) {

        // i

        dfs(nums, 0);

        return res;

    }

private:

    vector<vector<int>> res; // 使用一个新的容器

    void dfs(vector<int> nums, int x) {

        if(x == nums.size() - 1) {

            res.push_back(nums); //添加排列方案

            return;

        }

        for(int i = x; i < nums.size();i++) {

            swap(nums[i], nums[x]); // 使用交换固定在某个位置是我没有想到的

            dfs(nums, x+1);

            swap(nums[i], nums[x]); // 恢复交换

        }

    }

};
看完解析后的问题
  1. 是如何确定使用深度优先搜索的 --根据数组排列的特点...
  2. 如何想清楚终止条件

78.子集

给你一个重复的数组nums,数组中的元素互不相同。返回改数组所有可能的子集,解集不能保安重复的子集。你可以按任意顺序返回解集。

思路分析

对于选择输入的nums,考虑到每个nums[i]是选还是不选,由此组合出2的n次方个不同的子集。dfs中的i绊脚石当前考虑到nums[i]选或者不选。

cpp 复制代码
class Solution { 
public:
	vector<vector<int>> subsets(vector<int>& nums) { 
		int n = nums.size();
		vector<vector<int>> ans;
		vector<int> pathl // 记录当前正在构造的子集
		
		// 选或者是不选
		auto dfs = [&](this auto&& dfs, int i) -> void {
			if(i == n) { 
				ans.push_back(path); // 把当前子集加入答案
				return;
			}
			
			// 不选nums[i].直接递归下一个
			path.push_back(nums[i]);
			dfs(i+ 1);
			path.pop_back(); // 回溯撤销选择
			
		};
		dfs(0);
		return ans;
	}
}

ans.push_back(path)

  • 将当前构造好的子集 path 添加到答案集合

  • 值拷贝,把当前 path 复制一份存入 ans。

    path.push_back(nums[i])

  • 把第 i 个数加入当前子集,代表选择这个数

    path.pop_back()

  • 回溯操作:撤销上一步的选择,恢复 path 状态。

  • 保证递归返回后,上层分支不受下层修改影响。

17.电话号码的字母组合

给定一个包含2-9的字符串,返回所有可能的字母组合。可以按任意顺序返回

数字到字母的映射如下,与电话按键相同,注意1不对应任何字母。

思路分析

题目是需要去求可能的字母组合?

给定一个4如何使得程序返回 g h i?

题解
cpp 复制代码
class Solution {

private:

    const string letterMap[10] = {

        "", // 0

        "", // 1

        "abc", // 2

        "def", // 3

        "ghi", // 4

        "jkl", // 5

        "mno", // 6

        "pqrs", // 7

        "tuv", // 8

        "wxyz", // 9

    };

public:

    vector<string> result; // 需要返回的结果一个容器

    string s; // 来收集叶子节点

    void backtracking(const string& digits, int index) { // 记录遍历第几个数字了。

        if(index == digits.size()) {

            result.push_back(s);

            return;

        }

        int digit = digits[index] - '0';//将index指向的数字转为int

        string letters = letterMap[digit];// 取数字对应的字符集

        for(int i = 0; i < letters.size();i++) {

            s.push_back(letters[i]); // 处理

            backtracking(digits, index + 1); // 递归处理

            s.pop_back();

        }

    }

    vector<string> letterCombinations(string digits) {

        s.clear();

        result.clear();

        if(digits.size() == 0) {

            return result;

        }

        backtracking(digits, 0);

        return result;

    }

};
看完题解后的问题
  1. 两个字母就是两个for循环,三个字符就是三个for循环,然后类推吗

  2. 数字和字母如何去映射呢

  3. 处理 1 和 * #的异常情况单层遍历的逻辑是什么样的

  4. 单层遍历的逻辑是什么样的

cpp 复制代码
int digit = digits[index] - '0'; // index指向的数字转换为int

string letters = letterMap[digit] 取数字对应的字符集

for(int i = 0; i < letters.size(); i++) {

  

    s.push_back(letters[i]);// 处理

    backtracking(digits, index + 1);

  

    s.pop_back();

}
  1. 终止条件是怎么样的. -- 当index等于输入的数字的时候。
cpp 复制代码
if (index == digits.size()) {
    result.push_back(s);
    return;
}
相关推荐
Liangwei Lin2 小时前
LeetCode 287. 寻找重复数
算法·leetcode·职场和发展
OCR_133716212753 小时前
护照OCR校验位技术解析:从算法逻辑到工程落地,筑牢证件核验安全线
人工智能·算法
Hello.Reader3 小时前
算法基础(十三)——随机算法为什么有时主动引入随机性
java·数据库·算法
老鱼说AI3 小时前
现代 LangChain 开发指南:从 LCEL 原理到企业级 RAG 与 Agent 实战
java·开发语言·人工智能·深度学习·神经网络·算法·机器学习
小许同学记录成长4 小时前
基于幅度形态与参数聚类的工作模式判别
python·算法·scikit-learn
gumichef4 小时前
二叉树_堆
算法
Liangwei Lin4 小时前
LeetCode 70. 爬楼梯
算法
洛水水4 小时前
【力扣100题】38.路径总和 III
算法·leetcode·深度优先
小侯不躺平.4 小时前
C++ Boost库【2】 --stringalgo字符串算法
linux·c++·算法