【 理论基础 | 递归遍历 | 迭代遍历】

理论基础
二叉树有两种主要的形式:满二叉树和完全二叉树

1.什么是满二叉树?
满二叉树是一种特殊的二叉树,其中除了叶子节点之外,每个节点都有两个子节点,并且所有叶子节点都在同一层上。这意味着从根节点到任意一个叶子节点的路径长度相同。

具体来说,满二叉树有以下几个性质:

  1. 每个非叶子节点都有且仅有两个子节点;
  2. 所有叶子节点都在同一层上;
  3. 除最后一层外,其他层的节点都被填满,最后一层的节点可以不满。

2.什么是完全二叉树?
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。

转自卡哥代码随想录(PS:图片源自卡哥代码随想录)

3.二叉搜索树

前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树。

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树
下面这两棵树都是搜索树
转自卡哥代码随想录
(PS:图片源自卡哥代码随想录)

4.平衡二叉搜索树
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

5.二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。
那么链式存储方式就用指针, 顺序存储的方式就是用数组。
顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。

链式存储如图:在这里插入图片描述
来看看如何顺序存储呢?
其实就是用数组来存储二叉树,顺序存储的方式如图:

**加粗样式**在这里插入图片描述

用数组来存储二叉树如何遍历的呢?
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2

但是用链式表示的二叉树,更有利于我们理解,所以一般我们都是用链式存储二叉树。
所以大家要了解,用数组依然可以表示二叉树。

6.二叉树的遍历方式
深度优先遍历:先往深走,遇到叶子节点再往回走。
广度优先遍历:一层一层的去遍历。
(这两种遍历是图论中最基本的两种遍历方式)

那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
深度优先遍历:前序遍历(递归法,迭代法)
中序遍历(递归法,迭代法)
后序遍历(递归法,迭代法)
广度优先遍历:层次遍历(迭代法)

在深度优先遍历中:有三个顺序,前中后序遍历, 有同学总分不清这三个顺序,经常搞混,我这里教大家一个技巧。
前序遍历:中左右
中序遍历:左中右
后序遍历:左右中
大家可以对着如下图,看看自己理解的前后中序有没有问题。
在这里插入图片描述

7.二叉树中深度优先和广度优先遍历实现方式
做二叉树相关题目,经常会使用递归的方式来实现深度优先遍历,也就是实现前中后序遍历,使用递归是比较方便的。
栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。

8.二叉树的定义

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

补充:二叉树是一种基础数据结构,很重要!

递归遍历重点(前后中序的递归写法)
每次写递归,都按照这三要素来写:

(1)确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
(2)确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
(3)确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

144.二叉树的前序遍历
题目:给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 :
输入:root = [1,null,2,3]
输出:[1,2,3]
代码

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
         List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {   //参数和返回值
        if (root == null) {
            return;
        }
        result.add(root.val);//中
        preorder(root.left, result);//左
        preorder(root.right, result);//右
    }
}

145. 二叉树的后序遍历
题目:给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。

示例 :
输入:root = [1,null,2,3]
输出:[3,2,1]
代码

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
         List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postorder(root.left, list);//左
        postorder(root.right, list);//右
        list.add(root.val);         //中

    }
}

94. 二叉树的中序遍历
题目:
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

示例 :
输入:root = [1,null,2,3]
输出:[1,3,2]
代码

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
          List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inorder(root.left, list);//左
        list.add(root.val);          //中   
        inorder(root.right, list);//右

    }
}

用迭代法解决:

144. 二叉树的前序遍历
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。
为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。
图像演示链接
(PS:图像转自卡哥代码随想录)

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
     List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right != null){
                stack.push(node.right);
            }
            if (node.left != null){
                stack.push(node.left);
            }
        }
        return result;
    }
}

其余中序遍历和后序遍历同理!
(PS:本博客总结内容部分摘自卡哥代码随想录且已经证得同意,详细内容见卡哥代码随想录网站)

加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值