开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
一、含义理解
1.1 深度优先遍历
深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或者图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。
其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次
图示:
1.2 广度优先遍历
广度优先搜索算法(英语:Breadth-First Search,缩写为BFS)。BFS是从根节点开始,沿着树的宽度遍历节点。如果所有节点均被访问,则算法终止。图示:
二、深度优先搜索相关示例
2.1 路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。叶子节点 是指没有子节点的节点。
思路:我们从根节点开始,依次去遍历它的左侧子节点,然后是右侧子节点,直到找到相应的结果,否则返回false
var hasPathSum = function(root, targetSum) {
if(!root) return 0
let ret = false
const dfs = (root,sum)=>{
if(!root) return
if(sum == targetSum && (!root.left && !root.right)){
ret = true
}
if(root.left) dfs(root.left,root.left.val +sum)
if(root.right) dfs(root.right,root.right.val +sum)
}
dfs(root,root.val)
return ret
};
2.2 路径总和Ⅱ
在2.1的问题上,要求找到所有给定目标的路径
思路:从2.1我们使用了深度优先遍历,在这个情况下,我们需要运用到回溯的相关知识。
- 我们需要dps时,需要三个参数,分别为root(当前节点),sum(已经过的路径和),path(路径)
- 在递归的过程中,我们向下查找,如果当sum=== targetSum时,我们需要将路径path深度copy放到ret数组中
- 递归完成函数后,我们需要pop()进行回溯
var pathSum = function(root, targetSum) {
if(!root) return []
let ret = []
const dps = (root,sum,path)=>{
path.push(root.val)
if(sum === targetSum && (!root.left && !root.right)){
ret.push([...path])
}
if(root.left) dps(root.left,root.left.val+sum,path)
if(root.right) dps(root.right,root.right.val+sum,path)
path.pop()
}
dps(root,root.val,[])
return ret
};
2.3 检查平衡性
实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两棵子树的高度差不超过 1。
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
分析:
- 高度:子树的高度,我们先要求树的高度,即遍历树的深度,
- 遍历:遍历左树结构和右树结构,判断是否平衡
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
if(!root) return true
const height = (root)=>{
if(root == null){
return 0
}
return Math.max(height(root.left),height(root.right))+1
}
if((Math.abs(height(root.left) - height(root.right)))<=1 && isBalanced(root.left) && isBalanced(root.right)){
return true
}
return false
};
三、广度优先算法
3.1 路径总和
与2.1 的题目一样,我们使用BFS来实现,查看两者的区别
思路:
- 维护一个队列记录当前节点,并根据当前节点搜索下一层
- 类似于队列的先进先出
var hasPathSum = function(root, targetSum) {
if (!root) return 0
const stack = [[root, root.val]]
while (stack.length) {
const [p,l] = stack.shift()
if(l == targetSum && !p.left && !p.right ) return true
if (p.left) stack.push([p.left,l+p.left.val])
if (p.right) stack.push([p.right, l+p.right.val])
}
return false
};
3.2 路径总和Ⅱ
广度优先搜索来进行解析:
3.1中我们已经写出基本代码,现在我们考虑的是如何存储路径信息,我们考虑通过map进行路径的存储
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} targetSum
* @return {number[][]}
*/
var pathSum = function (root, targetSum) {
if (!root) return [];
const getParentNode = (root) => {
let path = [];
let cur = root;
while (cur) {
path.unshift(cur.val);
cur = hashParentNode.get(cur);
}
res.push(path);
};
const stack = [[root, root.val]];
const res = [];
const hashParentNode = new Map();
while (stack.length) {
const [p, l] = stack.shift();
if (l == targetSum && !p.left && !p.right) {
//需要获取路径信息
getParentNode(p);
continue;
}
if (p.left) {
hashParentNode.set(p.left, p);
stack.push([p.left, l + p.left.val]);
}
if (p.right) {
hashParentNode.set(p.right, p);
stack.push([p.right, l + p.right.val]);
}
}
return res;
};