二叉树相关知识

目录

一.基础

1. 定义

2. 二叉树的特点

3. 二叉树的类型

(1) 满二叉树:

(2) 完全二叉树:

(3) 斜二叉树:

 (4) 二叉搜索树(Binary Search Tree,BST)

(5)平衡二叉搜索树(Balanced Binary Search Tree)

4. 二叉树的遍历

(1)深度优先遍历

(2)广度优先遍历

5. 二叉树的存储结构

• 顺序存储结构

• 链式存储结构

6. 二叉树的性质

7. 二叉树的构建

5. 二叉树的应用

二.拓展

1. 二叉树的操作及算法复杂度

2. 二叉树的平衡问题

3.Java中的二叉树实现

(1). 定义二叉树节点类

(2). 构建二叉树

(3). 二叉树的遍历实现

• 前序遍历:

• 中序遍历:

• 后序遍历:

(4). 其他操作


一.基础

1. 定义

二叉树是一种树形结构,它是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。

2. 二叉树的特点

a.递归性:二叉树的定义是递归的,即二叉树可以用其自身来定义。一个二叉树要么为空,要么由一个根节点、一棵左子树和一棵右子树组成,左子树和右子树也都是二叉树。

b.节点结构:二叉树中的每个节点包含数据元素以及指向左子树和右子树的指针(如果存在的话)。

3. 二叉树的类型

(1) 满二叉树:

如果一棵二叉树的所有叶节点都在同一层,并且每个非叶节点都有两个子节点,那么这棵二叉树就是满二叉树。例如:

(2) 完全二叉树:

1. 定义

• 完全二叉树是一种特殊的二叉树。设二叉树的深度为h(根节点所在层为第1层)。

• 对于完全二叉树,除了第h层外,其它各层的节点数都达到最大个数,即第i层有(2的i-1次方)个节点。

• 在第h层上的节点都集中在最左边,这意味着第h层节点是从左向右连续存在的。

2. 示例

• 例如,下面这棵二叉树就是完全二叉树:

      1
    /   \
   2     3
  / \   /
 4   5 6

• 而下面这棵二叉树不是完全二叉树:

      1
    /   \
   2     3
    \     \
     4     5

• 因为在第二层中,节点2的右子树节点4的位置不符合完全二叉树的要求(完全二叉树要求节点是按层序从左到右依次排列的)。

(3) 斜二叉树:

是一种特殊的二叉树,所有节点都只有左子树或者所有节点都只有右子树。例如,所有节点都只有左子树的斜二叉树:

1
 \
  2
   \
    3
     \
      4

 (4) 二叉搜索树(Binary Search Tree,BST)

(a)定义

二叉搜索树是一种特殊的二叉树,它满足以下性质:对于树中的任意一个节点,其左子树中的所有节点的值都小于该节点的值,其右子树中的所有节点的值都大于该节点的值。

(b)节点插入操作

插入一个新节点时,首先从根节点开始比较。如果新节点的值小于当前节点的值,则向左子树方向继续比较;如果新节点的值大于当前节点的值,则向右子树方向继续比较。直到找到合适的空位置插入新节点。

(c)节点查找操作

查找一个值时,也是从根节点开始。如果要查找的值等于当前节点的值,则查找成功;如果要查找的值小于当前节点的值,则在左子树中继续查找;如果要查找的值大于当前节点的值,则在右子树中继续查找。如果最终未找到匹配的节点,则查找失败。

(d)节点删除操作

情况较为复杂。如果要删除的节点是叶节点(没有子节点),直接删除即可;如果要删除的节点只有一个子节点,那么将这个子节点取代被删除节点的位置;如果要删除的节点有两个子节点,通常的做法是用该节点右子树中的最小值(即右子树中最左边的节点)或者左子树中的最大值(即左子树中最右边的节点)来替换被删除的节点,然后再删除用于替换的那个节点。

(e)时间复杂度

在理想情况下,二叉搜索树的高度为 O(logn)(n为树中节点个数),此时查找、插入和删除操作的时间复杂度都是 O(logn)。但在最坏情况下,例如当二叉搜索树退化为斜二叉树(所有节点都只有左子树或者所有节点都只有右子树)时,树的高度为O(n),这些操作的时间复杂度也会退化为O(n)。

(5)平衡二叉搜索树(Balanced Binary Search Tree)

(a)定义

平衡二叉搜索树是一种特殊的二叉搜索树,它在满足二叉搜索树性质的基础上,还保证树的高度是相对平衡的。不同类型的平衡二叉搜索树有不同的平衡定义。

(b)目的

为了克服二叉搜索树在最坏情况下时间复杂度退化为O(n)的问题,平衡二叉搜索树通过自动调整树的结构来保持树的高度在 O(logn)范围内,从而保证查找、插入和删除操作的时间复杂度始终为 O(logn)。

(c)常见类型

• AVL树

平衡因子:AVL树中每个节点都有一个平衡因子,定义为左子树高度减去右子树高度的值,这个值只能是0、1或者-1。

调整操作:当插入或删除节点导致平衡因子超出这个范围时,就需要通过旋转操作(左旋、右旋、先左旋后右旋、先右旋后左旋)来重新平衡树。例如,在插入一个节点后,如果某个节点的平衡因子变为2或-2,就需要进行调整。

• 红黑树

节点颜色属性:红黑树的每个节点除了有数据域、左指针域、右指针域外,还有一个颜色属性,节点颜色可以是红色或者黑色。

红黑性质:

根节点是黑色的。

每个叶节点(空节点)是黑色的。

如果一个节点是红色的,则它的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有两个连续的红色节点)。

从任一节点到其每个叶节点的所有路径上包含相同数目的黑色节点。

调整操作:当插入或删除节点破坏了红黑性质时,需要通过变色和旋转操作来恢复红黑性质,从而保持树的平衡。

4. 二叉树的遍历

(1)深度优先遍历

• 前序遍历:先访问根节点,然后前序遍历左子树,再前序遍历右子树。例如对于二叉树:

      1
    /   \
   2     3
  / \   / \
 4   5 6   7

前序遍历的结果是:1、2、4、5、3、6、7。

• 中序遍历:先中序遍历左子树,然后访问根节点,再中序遍历右子树。上述二叉树的中序遍历结果是:4、2、5、1、6、3、7。

• 后序遍历:先后序遍历左子树,然后后序遍历右子树,最后访问根节点。该二叉树的后序遍历结果是:4、5、2、6、7、3、1。

(2)广度优先遍历

• 层序遍历:按层次顺序,从根节点开始,逐层向下遍历二叉树,同一层的节点按照从左到右的顺序访问。上述二叉树的层序遍历结果是:1、2、3、4、5、6、7。

5. 二叉树的存储结构

• 顺序存储结构

对于完全二叉树,可以采用顺序存储结构。将二叉树的节点按照从上到下、从左到右的顺序依次存储在数组中。

节点在数组中的下标具有一定的规律,设根节点的下标为0,如果某个节点的下标为i,那么它的左子节点的下标为2i + 1,右子节点的下标为2i+2。

但是这种存储方式对于非完全二叉树会造成空间浪费,因为要保证逻辑上二叉树的结构,可能需要用空节点来填补数组中的某些位置。

• 链式存储结构

二叉链表:每个节点包含三个域,数据域、左指针域和右指针域。左指针指向该节点的左子节点,右指针指向该节点的右子节点。这种结构简单灵活,是二叉树最常用的存储方式。

三叉链表:在二叉链表的基础上增加一个父指针域,用于指向该节点的父节点。这种结构在某些需要回溯到父节点的操作中比较方便,例如在计算二叉树的高度、求最近公共祖先等操作中。

6. 二叉树的性质

• 二叉树第i层上的最大节点数为 (2的i-1次方(i>=1))。

• 深度为k的二叉树的最大节点数为 (2的k次方-1(k>=1))。

• 对任何一棵二叉树,如果其叶节点个数为n0,度为2的节点个数为n2,则n0 = n2 + 1。可以通过分析二叉树中节点的度数关系(总度数+1 = 节点总数)来推导这个性质。

7. 二叉树的构建

可以根据给定的遍历序列来构建二叉树。但是需要注意的是,仅通过一种遍历序列(如中序遍历、前序遍历或后序遍历中的一种)不能唯一确定一棵二叉树。但是如果已知中序遍历序列和前序遍历序列,或者中序遍历序列和后序遍历序列,则可以唯一确定一棵二叉树。

• 例如,已知前序遍历序列和中序遍历序列构建二叉树的过程:

• 首先,前序遍历的第一个节点为根节点。

• 在中序遍历中,将序列分为两部分,左边是左子树的中序遍历序列,右边是右子树的中序遍历序列。

• 在前序遍历中,是左子树的前序遍历序列,是右子树的前序遍历序列。

• 然后分别对左子树和右子树递归地进行构建。

5. 二叉树的应用

在数据结构和算法中广泛应用,例如用于构建二叉搜索树(BST),以实现高效的查找、插入和删除操作;在表达式树中表示算术表达式;在哈夫曼树中用于数据压缩等。

二.拓展

1. 二叉树的操作及算法复杂度

(1) 插入节点

在二叉链表存储的二叉树中插入节点操作相对复杂,需要考虑插入的位置(作为叶节点插入或者替换某个节点等情况)。如果插入节点的操作是作为叶节点插入到二叉搜索树(一种特殊的二叉树)中,平均时间复杂度为O(logn)(为树中节点个数),最坏情况(树退化为斜二叉树)为O(n)。

(2) 删除节点

同样在二叉搜索树中,删除节点根据节点的度数(0度、1度或2度)不同有不同的处理方式。平均时间复杂度为O(logn),最坏情况为O(n)。

(3) 查找节点

在二叉搜索树中,根据节点的值进行查找。平均时间复杂度为O(logn),最坏情况为O(n)。如果是普通二叉树,查找操作可能需要遍历整棵树,时间复杂度为O(n)。

2. 二叉树的平衡问题

• 在二叉搜索树中,如果插入和删除操作不当,可能会导致树变得不平衡,例如形成斜二叉树,这会使查找、插入和删除操作的时间复杂度退化为O(n)。

• 为了解决这个问题,有多种平衡二叉树的算法,如AVL树和红黑树。AVL树通过调整节点的平衡因子(左子树高度与右子树高度之差),在插入和删除节点时进行旋转操作(左旋、右旋、先左旋后右旋、先右旋后左旋)来保持树的平衡,使得树的高度始终保持在O(logn),从而保证操作的时间复杂度为O(logn)。红黑树则通过定义节点的颜色(红色或黑色),并在插入和删除节点时通过变色和旋转操作来保持树的平衡。

3.Java中的二叉树实现

(1). 定义二叉树节点类

• 首先,需要定义二叉树的节点类,节点类包含节点的值以及指向左子节点和右子节点的引用。• 以下是一个简单的Java代码示例:

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

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

(2). 构建二叉树

• 可以通过手动创建节点并连接它们来构建二叉树。例如,构建一个简单的二叉树:

public class BinaryTreeExample {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
    }
}

(3). 二叉树的遍历实现

• 前序遍历:

• 递归实现:

public static void preorderTraversal(TreeNode root) {
        if (root!= null) {
            System.out.print(root.val + " ");
            preorderTraversal(root.left);
            preorderTraversal(root.right);
        }
    }

• 非递归实现(使用栈):

    public static void preorderTraversalNonRecursive(TreeNode root) {
        if (root == null) {
            return;
        }
        java.util.Stack<TreeNode> stack = new java.util.Stack<>();
        stack.push(root);
        while (!stack.empty()) {
            TreeNode node = stack.pop();
            System.out.print(node.val + " ");
            if (node.right!= null) {
                stack.push(node.right);
            }
            if (node.left!= null) {
                stack.push(node.left);
            }
        }
    }
• 中序遍历:

• 递归实现:

    public static void inorderTraversal(TreeNode root) {
        if (root!= null) {
            inorderTraversal(root.left);
            System.out.print(root.val + " ");
            inorderTraversal(root.right);
        }
    }

• 非递归实现(使用栈):

    public static void inorderTraversalNonRecursive(TreeNode root) {
        if (root == null) {
            return;
        }
        java.util.Stack<TreeNode> stack = new java.util.Stack<>();
        TreeNode current = root;
        while (current!= null ||!stack.empty()) {
            while (current!= null) {
                stack.push(current);
                current = current.left;
            }
            current = stack.pop();
            System.out.print(current.val + " ");
            current = current.right;
        }
    }
• 后序遍历:

• 递归实现:

    public static void postorderTraversal(TreeNode root) {
        if (root!= null) {
            postorderTraversal(root.left);
            postorderTraversal(root.right);
            System.out.print(root.val + " ");
        }
    }

• 非递归实现(使用栈和一个额外的变量记录上一次访问的节点):

    public static void postorderTraversalNonRecursive(TreeNode root) {
        if (root == null) {
            return;
        }
        java.util.Stack<TreeNode> stack = new java.util.Stack<>();
        TreeNode lastVisited = null;
        while (root!= null ||!stack.empty()) {
            while (root!= null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            // 如果右子节点为空或者已经访问过,则访问该节点
            if (root.right == null || root.right == lastVisited) {
                System.out.print(root.val + " ");
                lastVisited = root;
                root = null;
            } else {
                stack.push(root);
                root = root.right;
            }
        }
    }

(4). 其他操作

• 例如计算二叉树的高度:

    public static int getHeight(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        return Math.max(leftHeight, rightHeight)+1;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值