预备知识

树的基本概念

树是一种由节点组成的数据结构,但它比链表更加高级。在链表中,一个节点连接着另一个节点,树也是由许多的节点构成的。唯一的区别就是一个树节点可以连接多个树节点,一颗树只有一个根节点,根节点作为起源,由它展开一个树状的数据结构。

在树中,每个节点都含有自己的数值,以及与之相连的子节点,连接节点的线叫做相连线(edge)

  • 图中A是根节点(root),同时也是B和C的父节点,即B和C为A的子节点,也称作孩子节点。
  • 叶子节点 没有儿子节点的节点。如图中的H、I、J、F、G
  • 兄弟节点 具有相同父亲的节点为兄弟
  • 子树 一个树由许许多多的子树(sub-tree)构成,每个节点加上它所有的子节点(包括子节点的子节点们)就是一个子树,如图中,D、H、和I就是能构成sub tree,B、D、E、H、I、和J也是一个子树。
  • 深度&高度 节点的高度(height),意味着此节点到尾节点之间相连线的数量。【注】最长的那条。图中,B的高度就是2,因为B到尾节点H之间的edge数量为2。节点的深度(depth),意味着此节点到根节点的edge数量【注】深度是唯一的。图中,D的深度是2,因为D到根节点A之间的edge数量是2。

树的一些定义

树的种类

树的种类有很多,其中排序二叉树是重点,其他的树类型稍作理解

  • 二叉树(Binary Tree):每个节点最多含有两个子节点,上面图示中的树就是二叉树。
  • 完全二叉树(Complete Binary Tree):假设一个二叉树深度(depth)为d(d > 1),除了第d层外,其它各层的节点数量均已达到最大值,且第d层所有节点从左向右紧密排列,这样的二叉树就是完全二叉树。
    满二叉树(Full Binary Tee):在满二叉树中,每个不是尾节点的节点都有两个子节点。
    满二叉树&完全二叉树
  • 排序二叉树(Binary Search Tree):在此树中,每个节点的数值比左子树上的每个节点都大,比所有右子树上的节点都小。
  • 平衡二叉树(AVL Tree):任何节点的两颗子树的高度差不大于1的二叉树。
    平衡二叉树&排序二叉树
  • B树(B-Tree):B树和平衡二插树一样,只不过它是一种多叉树(一个节点的子节点数量可以超过二)。
    B树
  • 红黑树(Red—Black Tree):是一种自平衡二叉寻找树。
    红黑树

二分查找树的实现

二分查找树(Binary Search Tree),也叫做排序二叉树。在这种树中,我们寻找一个特定的数值非常容易,因为二分查找树满足以下的特性:每个节点都比自己左子树上的节点大,并比右子树上的节点小。如果我们想要寻找一个特定的元素,只需要依赖其特性,顺着特定的路径就能找到目标。在此树中,搜索、插入和删除的复杂度等于树高,往往就是O(logN),非常合适用来存储数据。

定义树和节点

1、树类包括:

  • 数值
  • 左孩子、右孩子 (这样好递归吧?俺猜的)
  • 初始化函数

2、再声明一个根节点

【疑问】

  • static是啥子哦
  • 初始化函数…全忘了
  • 这个根节点的声明方式?
static class TreeNode {
        public int value;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(int value) {
            this.value = value;
        }
    }

    public TreeNode root;

在树中插入一个值 insert

  • 整体思路
    我们用 current 来寻找要插入的位置,通过比较数值大小(与 parent 比较)不断地迭代遍历左子树或右子树以找到合适的位置。由于二分查找树中不存在两个相同的值,因此若 current 为空,则插入。🔚
public void insert(int key) {
        if(root == null) { //根节点为空,直接插入
            root = new TreeNode(key);
            return;
        }
        TreeNode current = root; //当前节点指向根节点
        TreeNode parent = null; //定义一个父节点
        while(true) { //将递归换成了这!
            parent = current;  //父亲节点跟上
            if(key < parent.value) { //若值比父亲节点小
                current = parent.left; //向左
                if(current == null) { //到底了
                    parent.left = new TreeNode(key); //插入
                    return;
                }
            } else if (key > parent.value){ //若值比父亲节点大
                current = parent.right; //向右
                if(current == null) { //到底了
                    parent.right = new TreeNode(key); //插入
                    return;
                }
            } else {
                return; // BST does not allow nodes with the same value
            }
        }
    }

在树中寻找一个节点 get

public TreeNode get(int key) {
        TreeNode current = root; //从根节点开始
        while(current != null && current.value != key) { //没找到 or 没到底
            if (key < current.value) { // 比当前值小
                current = current.left; //向左
            } else if (key > current.value) { //比当前值大
                current =current.right; //向右
            }
        } //结束了即一个条件满足
        return current == null ? null : current; //分别返回对应的情况
    }

在树中删去一个节点 delete

  • 整体分析
    1、要删除的是尾节点
    2、要删除的节点只有一个孩子
    3、要删除的节点有两个孩子
    在这里插入图片描述
public boolean delete(int key) {
    TreeNode parent = root; //从根节点开始
    TreeNode current = root; //从根节点开始
    boolean isLeftChild = false; //先往后看
    while(current != null && current.value != key) { //找到这个节点节点
        parent = current; //父亲节点跟上!
        if(current.value  > key) { //向左找
            isLeftChild = true; //更新
            current = current.left; 
        } else {
            isLeftChild = false; //更新
            current = current.right;
        }
    }
    if(current == null) { //找不到这个节点,报错
        return false;
    }
    // Case 1: if node to be deleted has no children
    if(current.left == null && current.right == null) { //是叶子节点
        if(current == root) { //如果找到的是根节点
            root = null; //删除
        } else if(isLeftChild) { //如果是父亲节点的左孩子
            parent.left = null; //删除
        } else {
            parent.right = null;
        }
    // Case 2: if node to be deleted has only one child
    } else if (current.right == null) { //右孩子为空
        if(current == root) { //是根节点
            root = current.left; //让其左孩子当根
        } else if (isLeftChild) { //如果是父亲节点的左孩子
            parent.left = current.left; //删除
        } else {
            parent.right = current.left; //删除
        }
    } else if (current.left == null) { //左孩子为空,只有右孩子
        if(current == root) { //该节点是根节点
            root = current.right; //让右孩子当根
        } else if (isLeftChild) { //如果是父亲节点的左孩子
            parent.left = current.right;
        } else {
            parent.right = current.right;
        }
    // Case 3: current.left != null && current.right != null
    } else { //有两个孩子
        TreeNode successor = getSuccessor(current);
        if (current == root) {
            root = successor; //将找到的节点作为根
        } else if (isLeftChild) { //如果是父亲节点的左孩子
            parent.left = successor;
        } else {
            parent.right = successor;
        }
        successor.left = current.left; //最后...看图吧
    }
    return true;
}
//找到右子树中最下最左的节点
private TreeNode getSuccessor(TreeNode node) { //找到当前节点 右子树的最小值
    TreeNode successor = null;
    TreeNode successorParent = null;
    TreeNode current = node.right; //右子树
    while (current != null) { //遍历找到右子树 左边
        successorParent = successor;
        successor = current;
        current = current.left; 
    }
    if (successor != node.right) { //此时 current 为空
        successorParent.left= successor.right;
        successor.right = node.right;
    }
    return successor;
}

树的遍历

  • 总述
    1、前序遍历
    2、中序遍历
    3、后续遍历

前序遍历 Preorder Traversal

在前序遍历中,先访问节点自己,然后访问左子树,最后再访问右子树。对于每个节点迭代此操作。根左右
在这里插入图片描述

public static void preOrderTraversal(TreeNode root) {
        if(root == null) { //如果树为空,本次遍历结束
            return;
        }
        System.out.println(root.value); //根
        preOrderTraversal(root.left); //左
        preOrderTraversal(root.right); //右
    }

中序遍历 Inorder Traversal

在中序遍历中,先访问左子树上的节点,再访问自己,最后再访问右子树上的节点。左根右
在这里插入图片描述

public static void inOrderTraversal(BST.TreeNode root) {
        if(root == null) {
            return;
        }
        inOrderTraversal(root.left);
        System.out.println(root.value); 
        inOrderTraversal(root.right);
    }

后序遍历 Postorder Traversal

在后序遍历中,先访问左右子树,最后再访问自己。左右根
在这里插入图片描述

public static void postOrderTraversal(TreeNode root) {
        if(root == null) {
            return;
        }
        postOrderTraversal(root.left);
        postOrderTraversal(root.right);
        System.out.println(root.value);
    }

力扣练习

958. 二叉树的完全性检验

给定一个二叉树,确定它是否是一个完全二叉树。

  • 别人的想法【跪】
    1、若按照广度搜索给二叉树编号的话,定义 root-----1,会发现:任何节点的左孩子----- index * 2,右孩子------- index * 2 + 1
    2、所以本题的核心其实就是 ➡️ 判断按照(1)中规则编号最终得出来的maxindex 是否等于 二叉树节点树
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值