深度优先搜索算法实现整理

深度优先搜索算法实现整理

深度优先搜索(DFS-Depth First Search)是很基础的二叉树搜索算法。很多二叉树的题型,都是基于此演变而来。

在这里整理一下三种遍历方式的递归与非递归实现,方便收藏。

三种遍历

  • 前序遍历:按照父节点、左节点、右节点的顺序遍历

  • 中序遍历:按照左节点、父节点、右节点的顺序遍历

  • 后序遍历:按照左节点、右节点、父节点的顺序遍历

所谓的前中后,实际上是父节点在遍历顺序中出现的位置是在前中后来区分的。

递归实现

这三种遍历,用递归都会非常简单。无非是根据前序、中序、后序将递归方法放到不同的位置。

/**
 * 递归遍历
 *
 * @param root
 */
public void recursiveTraversal(TreeNode root) {
    // 前序
    List<Integer> preorderList = new ArrayList<>();
    preorderTraversal(root, preorderList);
    // 中序
    List<Integer> inorderList = new ArrayList<>();
    inorderTraversal(root, inorderList);
    // 后序
    List<Integer> postorderList = new ArrayList<>();
    postorderTraversal(root, postorderList);
}

/**
 * 前序优先遍历(递归)
 *
 * @param root
 * @param list
 */
public void preorderTraversal(TreeNode root, List<Integer> list) {
    if (root == null) {
        return;
    }
    list.add(root.val);
    preorderTraversal(root.left, list);
    preorderTraversal(root.right, list);
}

/**
 * 中序优先遍历(递归)
 *
 * @param root
 * @param list
 */
public void inorderTraversal(TreeNode root, List<Integer> list) {
    if (root == null) {
        return;
    }
    inorderTraversal(root.left, list);
    list.add(root.val);
    inorderTraversal(root.right, list);
}

/**
 * 后序优先遍历(递归)
 *
 * @param root
 * @param list
 */
public void postorderTraversal(TreeNode root, List<Integer> list) {
    if (root == null) {
        return;
    }
    postorderTraversal(root.left, list);
    postorderTraversal(root.right, list);
    list.add(root.val);
}

非递归实现

前序遍历

前序遍历利用栈结构来实现。因为前序遍历的顺序是中左右,所以在压入栈的时候,需要记录到数组中,之后再继续将做节点压入栈。

/**
 * 前序遍历(非递归)
 * 使用栈,先记录当前节点,之后入栈。出栈的时候不记录。
 *
 * @param root
 */
public List<Integer> preorderTraversal2(TreeNode root) {
    List<Integer> inorderList = new ArrayList<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode current = root;
    while (current != null || !stack.isEmpty()) {
        // 当前指针的节点不为空,则入栈
        while (current != null) {
            // 先记录当前节点父节点
            inorderList.add(current.val);
            // 当前节点入栈
            stack.push(current);
            // 因为是前序遍历,顺序为父节点、左子节点、右子节点,因此指针移动到左节点
            current = current.left;
        }

        // 当前节点为空,则说明本侧子树遍历到底了,需要将父节点出栈
        current = stack.pop();
        // 将右节点入栈
        current = current.right;
    }
    return inorderList;
}

中序遍历

中序遍历利用栈结构来实现。因为中序遍历的顺序是左中右,所以要一直把左节点压入栈,之后出栈的时候再记录到数组中。这里和前序遍历是不一样的。

/**
 * 中序优先遍历(非递归)
 * 使用栈,一直先遍历左节点,左节点到头后,出栈记录父节点,然后再从右节点开始继续遍历右节点的左子节点 
 *
 * @param root
 */
public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> inorderList = new ArrayList<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode current = root;
    while (current != null || !stack.isEmpty()) {
        // 当前指针的节点不为空,则入栈
        while (current != null) {
            stack.push(current);
            // 一直将左指针压入栈
            current = current.left;
        }

        // 当前节点为空,则说明本侧子树遍历到底了,需要将父节点出栈
        current = stack.pop();
        // 记录父节点
        inorderList.add(current.val);
        // 将右节点入栈
        current = current.right;
    }
    return inorderList;
}

后序遍历

也是使用栈,但是要稍微复杂一些。因为你在记录当前节点时,需要考虑,当前节点的右节点是否已经记录过。

/**
 * 后序优先遍历(非递归)
 * 也是使用栈
 *
 * @param root
 * @return
 */
public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> postorderList = new ArrayList<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode current = root;
    // 后续在判断当前节点是否有右子节点时,需要用一个指针辅助判断是否已经记录过他的右子节点
    TreeNode pre = null;
    while (current != null || !stack.isEmpty()) {
        while (current != null) {
            // 持续将左节点入栈
            stack.push(current);
            current = current.left;
        }

        // 左节点到头,再看是否有右节点。同时要保证右节点没有记录过
        current = stack.peek();
        if (current.right != null && current.right != pre) {
            current = current.right;
        } else {
            // 如果没有右节点,则记录当前节点
            postorderList.add(current.val);
            // 出栈
            pre = stack.pop();
            // 这里置为空,可以在下次循环时,不进入子循环
            current = null;
        }
    }
    return postorderList;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白码上飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值