🚍:感兴趣的朋友,赶紧上车吧!!
🎉欢迎关注🔍点赞👍收藏🎇留言📙
🎄有任何疑问,欢迎留言讨论!!!
回溯算法框架
一个回溯问题,实际上就是一个决策树遍历的过程,我们只需要考虑其中的三个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
接下来我将用全排列和N皇后问题来帮你理解词语的意思,准备发车
全排列问题
[这是leetcode链接](https://leetcode-cn.com/problems/permutations/)
对于全排列问题我相信大家并不陌生,对于n个不重复的数,全排列共有n!个。
比方说现在有3个数[1,2,3]你肯定不会随便给他乱排吧,一般我们的想法会是这样的:
首先固定第一位数1,然后第二位数是2,最后一位数就只能是2,然后可以把第二位变成 3,最后一位数就只能是2了;然后在变化第一位数为2,第二位数为1,最后一位为3…
我们的这种想法其实就是回溯算法,我们可以画一张简单的回溯树来看一下:
只要我们从这棵树的根部遍历这棵树,记录上面的数字我们就可以得到所有的全排列,当你站在根节点的时候你可以选择1,2,3这三条不同的树枝。假设你选择了1这条树枝,之后你的选择就不能回退了,之后的选择就只有2和3了,1这个树枝在你身后,这个选择你之前做过了,而全排列是不允许重复使用数字的。
现在可以解答开头的几个名词:[2]
就是「路径」,记录你已经做过的选择;[1,3]
就是「选择列表」,表示你当前可以做出的选择;「结束条件」就是遍历到树的底层,在这里就是选择列表为空的时候。
接下来就上代码框架了
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
全排列代码:
List<List<Integer>> res = new LinkedList<>();
/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
// 记录「路径」
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, track);
return res;
}
// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track) {
// 触发结束条件
if (track.size() == nums.length) {
res.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length; i++) {
// 排除不合法的选择
if (track.contains(nums[i]))
continue;
// 做选择
track.add(nums[i]);
// 进入下一层决策树
backtrack(nums, track);
// 取消选择
track.removeLast();
}
}
BFS算法框架
问题的本质就是让你在一幅「图」中找到从起点start
到终点target
的最近距离,其实bdfs都是在找这种最短路径的事。
直接上框架:
// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {
Queue<Node> q; // 核心数据结构
Set<Node> visited; // 避免走回头路
q.offer(start); // 将起点加入队列
visited.add(start);
int step = 0; // 记录扩散的步数
while (q not empty) {
int sz = q.size();
/* 将当前队列中的所有节点向四周扩散 */
for (int i = 0; i < sz; i++) {
Node cur = q.poll();
/* 划重点:这里判断是否到达终点 */
if (cur is target)
return step;
/* 将 cur 的相邻节点加入队列 */
for (Node x : cur.adj())
if (x not in visited) {
q.offer(x);
visited.add(x);
}
}
/* 划重点:更新步数在这里 */
step++;
}
}
二叉树的最小高度问题
int minDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
// root 本身就是一层,depth 初始化为 1
int depth = 1;
while (!q.isEmpty()) {
int sz = q.size();
/* 将当前队列中的所有节点向四周扩散 */
for (int i = 0; i < sz; i++) {
TreeNode cur = q.poll();
/* 判断是否到达终点 */
if (cur.left == null && cur.right == null)
return depth;
/* 将 cur 的相邻节点加入队列 */
if (cur.left != null)
q.offer(cur.left);
if (cur.right != null)
q.offer(cur.right);
}
/* 这里增加步数 */
depth++;
}
return depth;
}
动态规划框架
经典问题有:
1.正则表达式问题
2. 最大子数组和问题
3. 最长递增序列问题
4. 背包问题
#初始化 base case
dp[0][0][.....] = base
#进行状态转移
for(状态1 in 状态1的所有取值);
for(状态2 in 状态2的所有取值);
for.....
dp[状态1][状态2][.....] = 求最值(选择1,选择2......)
二分框架
bool check(int x){}
ans=表示不存在的值
low=最小可能的值
high=最大可能的值
while(low<=high){
mid =(low+high)/2;
if(check(mid)){
ans=mid;
low=mid+1;
}else{
high=mid-1;
}
}
典型的使用场景: 要求我们求出某种条件的最大值的最小可能情况或者最小值的最大情况
使用前提:
1. 答案在一个固定的区间内
2. 难以通过搜索来找到符合要求的值, 但给定一个值你可以很快的判断它是不是符合要求
3. 可行解对于区间要符合单调性, 因为有序才能二分嘛
典型的使用场景: 要求我们求出某种条件的最大值的最小可能情况或者最小值的最大情况
使用前提:
1. 答案在一个固定的区间内
2. 难以通过搜索来找到符合要求的值, 但给定一个值你可以很快的判断它是不是符合要求
3. 可行解对于区间要符合单调性, 因为有序才能二分嘛
希望大家可以好好的刷算法题哦!!!!!!!!