算法---- 深度优先遍历与广度优先遍历

310 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

一、含义理解

1.1 深度优先遍历

深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或者图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次

图示:

image.png

1.2 广度优先遍历

广度优先搜索算法(英语:Breadth-First Search,缩写为BFS)。BFS是从根节点开始,沿着树的宽度遍历节点。如果所有节点均被访问,则算法终止。图示:

image.png

二、深度优先搜索相关示例

2.1 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。叶子节点 是指没有子节点的节点。

image.png 思路:我们从根节点开始,依次去遍历它的左侧子节点,然后是右侧子节点,直到找到相应的结果,否则返回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的问题上,要求找到所有给定目标的路径

image.png

思路:从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;
};