面试官:HashMap了解吗?回答balabala。那你讲讲红黑树吧!

1. 二叉树基本概念

二叉树有哪几种存储方式?什么样的二叉树适合用数组来存储?

1.1 什么是树(Tree)?

树的相关概念:节点(Node)、高度(Height)、深度(Depth)、层(Level)。
在这里插入图片描述
节点的高度:节点到叶子节点的最长路径(边数);
节点的深度:根节点到这个节点所经历的边的个数;
节点的层数:节点的深度+1;
树的高度:根节点的高度;

以上图树为例:A节点的高度为2,深度为1,层数为2,树的高度为3;

在这里插入图片描述

1.2 什么是二叉树(Binary Tree)?

二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点。

在这里插入图片描述
上面这个图里面,有两个比较特殊的二叉树,分别是编号 2 和 编号 3。

  • 满二叉树
    如上图编号 2,叶子节点全都在最底层,每个非叶子节点都有左右两个子节点,这种二叉树就叫作满二叉树。

  • 完全二叉树
    如上图编号 3,叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫作完全二叉树。

    (完全二叉树其实就相当于满二叉树从后往前摘除几个叶子节点。)

1.3 二叉树的存储

想要存储一棵二叉树,有两种方法,一种是基于指针或者引用的二叉链式存储法,一种是基于数组的顺序存储法。

  • 链式存储法
    每个节点有三个字段,其中一个存储数据,另外两个是指向左右子节点的指针。大部分的二叉树代码都是通过这种结构来实现的。
    在这里插入图片描述
  • 基于数组的顺序存储法
    我们把根节点存储在下标 i = 1 的位置,那左子节点就存储在下标 2*i = 2 的位置,右子节点存储在 2*i+1 = 3 的位置。
    在这里插入图片描述
    一般情况下,为了方便计算子节点,根节点会存储在下标为 1 的位置,这样就可以通过下标计算,把整棵树都串起来。

1.4 二叉树的遍历

(深圳开思时代面试题)

  • 树的遍历方式有哪几种?
  • 图的遍历方式呢?

二叉树的遍历方法有三种:前序遍历、中序遍历和后序遍历。前中后表示的是节点与它的左右子树节点遍历的先后顺序。如下图所示。
在这里插入图片描述
前序遍历,其实就是先遍历根节点,然后再递归的遍历左子树,最后递归地打印右子树。所以,我们可以把前、中、后序遍历的递推公式都写出来。

// 前序遍历的递推公式
preOrder(r) = print r -> preOrder(r->left) -> preOrder(r->right)
// 中序遍历的递推公式
inOrder(r) = inOrder(r->left) -> print r -> inOrder(r->right)
// 后序遍历的递推公式
postOrder(r) = postOrder(r->left) -> postOrder(r->right) -> print r

前序遍历的伪代码如下:

void preOrder(Node* root) {
    if (root == null) return;
    print root;
    preOrder(root->left);
    preOrder(root->right);
}

2. 二叉查找树

二叉查找树也叫二叉搜索树,是为了实现快速查找而生的。二叉查找树要求,在树中任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。

二叉查找树不仅仅支持快速查找数据,还支持快速插入、删除一个数据。它是怎么做到这些的呢?

2.1 二叉查找树的查找操作

我们先取根节点,如果要查找的数据比根节点值小,那就在左子树中递归查找;如果要查找的数据比根节点值大,那就在右子树中递归查找。
在这里插入图片描述
查找的代码实现如下:

public class BinarySearchTree {
    private Node tree;
    
    public Node find(int data) {
        Node p = tree;
        while (p != null) {
            if (data < p.data) {
                p = p.left;
            } else if (data > p.data) {
                p = p.right;
            } else {
                return p;
            }
        }
        return null;
    }
    
    public static class Node {
        private int data;
        private Node left;
        private Node right;
        
        public Node(int data) {
            this.data = data;
        }
    }
}

2.2 二叉查找树的插入操作

二叉查找树的插入过程有点类似查找操作。
在这里插入图片描述
插入的代码实现如下:

public void insert(int data) {
    // 树中没有节点
    if (tree == null) {
        tree = new Node(data);
        return;
    }
    Node p = tree;
    while (p != null) {
        if (data > p.data) {
            if (p.right == null) {
                p.right = new Node(data);
                return;
            }
            p = p.right;
        } else { // data < p.data
            if (p.left == null) {
                p.left = new Node(data);
                return;
            }
            p = p.left;
        }
    }
}

2.3 二叉查找树的删除操作

针对要删除节点的子节点个数的不同,需要分三种情况来处理。

第一种情况是,如果要删除的节点没有子节点,直接将父节点中指向要删除节点的指针置为 null。如下图中删除节点 55;

第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),需要更新父节点中指向要删除节点的指针,让它指向要删除节点的子节点就可以了。如下图中删除节点 13;

第三种情况是,如果要删除的节点有两个子节点,这就比较复杂了。
我们需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,最小节点肯定没有左子节点(如果有左子节点,那就不是最小节点了),所以,我们可以应用上面两条规则来删除这个最小节点。如下图中删除节点 18;
在这里插入图片描述
删除的代码实现如下:

public void delete(int data) {
    Node p = tree; // p指向要删除的节点,初始化指向根节点
    Node pp = null; // pp记录的是p的父节点
    while (p != null && p.data != data) {
        pp = p;
        if (data > p.data) {
            p = p.right;
        } else {
            p = p.left;
        }
    }
    if (p == null) { // 没有找到
        return;    
    }
    // 要删除的节点有两个子节点
    if (p.left != null && p.right != null) { // 查找右子树中最小节点
        Node minP = p.right;
        Node minPP = p; // minPP表示minP的父节点
        while (minP.left != null) {
            minPP = minP;
            minP = minP.left;
        }
        p.data = minP.data; // 将minP的数据替换到p中
        p = minP; // 下面就变成了删除minP了
        pp = minPP;
    }
    //删除节点是叶子节点或者仅有一个子节点
    Node child; // p的子节点
    if (p.left != null) {
        child = p.left;
    } else if (p.right != null) {
        child = p.right;
    } else {
        child = null;
    }
    // 删除的是根节点
    if (pp == null) {
        tree = child;
    } else if (pp.left == p) {
        pp.left = child;
    } else {
        pp.right = child;
    }
}

3. 红黑树(Red-Black Tree)

红黑树的英文是 “Red-Black Tree”,简称 R-B Tree。

3.1 如何定义一棵"红黑树"?

红黑树的节点,一类被标记为黑色,一类被标记为红色。除此之外,一棵红黑树还需要满足这样几个要求:

  • 根节点是黑色的;

  • 每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;

    主要是为了简化红黑树的代码实现而设置的,画图的时候将黑色的、空的叶子节点都省略掉了。

  • 任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的;

  • 每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点;
    在这里插入图片描述

(广州金财互联面试题)
能说说二叉树、二叉查找树、红黑树这几种数据结构的区别吗?

参考资料

极客时间《数据结构与算法之美》第23、24、25讲

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值