The best way to end your fear is to face it yourself. 
结束恐惧的最佳办法就是自己面对。

本分分享了二叉搜索树的几种实现,由简入繁。说句题外话,马上又是金三银四的季节了,无论跳不跳槽,增加自己的知识储备总是没错的。从代码里读原理,读思想。

所有源码均已上传至github:链接

日志

2019年3月28日 AVL树

准备

public class Node {/*** data*/public int data;/*** 左节点*/public Node left;/*** 右节点*/public Node right;public Node(int data) {this.data = data;}}复制代码

实现一个支持插入、删除、查找操作的二叉查找树,并且找二叉树的最大值和最小值操作

插入操作

传入一个data值,当tree为null,初始化tree,否则遍历tree,比较data与node.data的值,大于的话插入右节点,否则插入左节点。

    private void insert(int data) {if (null == tree) {tree = new Node(data);return;}Node node = tree;while (null != node) {if (data > node.data) {if (null == node.right) {node.right = new Node(data);return;}node = node.right;} else {if (null == node.left) {node.left = new Node(data);return;}node = node.left;}}}复制代码

查找

查找的实现也比较简单,遍历tree的左右节点,直到找到data == node.data

    private Node find(int data) {Node node = tree;while (null != node) {if (data > node.data) {node = node.right;} else if (data < node.data) {node = node.left;} else {return node;}}return null;}复制代码

找二叉树的最大值

找二叉树的最大值最小值比较简单,按照树的结构遍历即可

    private Node findMaxNode() {if (null == tree) return null;Node node = tree;while (null != node.right) {node = node.right;}return node;}复制代码

找二叉树的最小值

    private Node findMinNode() {if (null == tree) return null;Node node = tree;while (null != node.left) {node = node.left;}return node;}复制代码

删除操作

删除是这里面最复杂的操作了,这里详细讲一下。

针对删除的节点的位置不同需要考虑以下三种情况:

  1. 如果要删除的节点没有子节点,只需要直接将父节点中,指向要删除节点的指针置为 null。
  2. 如果要删除的节点只有一个子节点(左子节点或者右子节点),只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。
  3. 如果要删除的节点有两个子节点,这就比较复杂了。需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点(如果有左子结点,那就不是最小节点了)。
    private void delete(int data) {Node node = tree;//node的父节点Node pNode = null;while (null != node && node.data != data) {pNode = node;if (data > node.data) {node = node.right;} else {node = node.left;}}if (null == node) return;if (null != node.left && null != node.right) {Node p = node.right;//p的父节点Node pp = node;while (null != p.left) {pp = p.left;p = p.left;}//删除操作node.data = p.data;node = p;pNode = pp;}Node child;if (null != node.left) {child = node.left;} else if (null != node.right) {child = node.right;} else {child = null;}if (null == pNode) {tree = child;} else if (pNode.left == null) {pNode.left = child;} else {pNode.right = child;}}复制代码

实现查找二叉查找树中某个节点的后继、前驱节点

ps:因为这里测试的节点都是根节点,因为比较简单,不需要遍历该节点的父节点。

后续如果需要,欢迎留言,我补充一下。

查找前驱节点

    private Node findPreNode(Node node) {if (null == node) return null;if (null != node.left){Node left = node.left;while (null != left.right){left = left.right;}return left;}return null;}复制代码

查找后继节点

    private Node findNextNode(Node node) {if (null == node) return null;if (null != node.right) {Node right = node.right;while (null != right.left) {right = right.left;}return right;}return null;}复制代码

实现二叉查找树前、中、后序以及层级遍历

前序遍历

    public void preOrderTraversal(Node tree) {if (null == tree) return;System.out.print(tree.data + " ");preOrderTraversal(tree.left);preOrderTraversal(tree.right);}复制代码

中序遍历

    public void inOrderTraversal(Node tree) {if (null == tree) return;inOrderTraversal(tree.left);System.out.print(tree.data + " ");inOrderTraversal(tree.right);}复制代码

后序遍历

    public void postOrderTraversal(Node tree) {if (null == tree) return;postOrderTraversal(tree.left);postOrderTraversal(tree.right);System.out.print(tree.data + " ");}复制代码

层级遍历

层级遍历的思想是维护一个队里,每一次遍历tree,将tree的节点打印,然后将tree的left和right节点存入队列里(每一次打印时从队列里取即可)。

    public void levelTraversal(Node tree) {if (null == tree) return;LinkedList<Node> linkedList = new LinkedList<>();Node node;linkedList.offer(tree);while (!linkedList.isEmpty()) {node = linkedList.poll();System.out.print(node.data + " ");if (null != node.left) {linkedList.offer(node.left);}if (null != node.right) {linkedList.offer(node.right);}}}复制代码

AVL树

定义

AVL树是一种平衡二叉树。貌似就windows对进程地址空间的管理用到了AVL树。

  1. 左右子树的高度差小于等于 1。
  2. 其每一个子树均为平衡二叉树。

核心

AVL树核心在于左旋和右旋上,当出现不平衡的情况时,它会自动调整。

声明一个AVL树的Node类

在普通二叉树的基础上新增了几个属性

  1. balance 是平衡标识,它的区间是[-1,1]
  2. height 是该节点的高度,自上而下计算,从0开始
    public class Node {/*** 数据*/int data;/*** 高度*/int height;/*** 平衡标识*/int balance;/*** 左子树*/Node left;/*** 右子树*/Node right;/*** 根*/Node parent;/*** 有参构造方法** @param data   数据* @param parent 父节点*/Node(int data, Node parent) {this.data = data;this.parent = parent;}}复制代码

插入

插入方法和常规插入的区别是需要将当前节点作为根节点插入,并且执行一下自平衡方法。

 private void insert(int data) {if (null == root) {root = new Node(data, null);} else {Node node = root;Node parent;while (node.data != data) {parent = node;boolean goleft = node.data > data;node = goleft ? node.left : node.right;if (null == node) {if (goleft) {parent.left = new Node(data, parent);} else {parent.right = new Node(data, parent);}rebalance(parent);break;}}}}复制代码

删除

这里是根据节点删除。和常规的删除相比,也是多了一步自平衡。

    private void delete(Node node) {if (null == node.left && null == node.right) {if (null == node.parent) {root = null;} else {Node parent = node.parent;if (parent.left == node) {parent.left = null;} else {parent.right = null;}rebalance(parent);}}if (null != node.left) {Node child = node.left;while (null != child.right) {child = child.right;}node.data = child.data;delete(child);} else {Node child = node.right;if (null != child) {while (null != child.left) {child = child.left;}node.data = child.data;delete(child);}}}复制代码

自平衡

有这么四种情况。

这里是否需要自平衡,是根据balance来判断的,当balance为-2的时候,(如果该节点的左子树的左子树的高度小于右子树高度,左旋,)右旋;当balance为2的时候,(如果该节点的右子树的右子树的高度小于左子树高度,右旋,)左旋;最后判断根节点是否为null,是则递归,否则赋值,停止递归;平衡完毕。

    private void rebalance(Node node) {setBalance(node);if (node.balance == -2) {if (getHeight(node.left.left) < getHeight(node.left.right)) {node.left = rotateLeft(node.left);}node = rotateRight(node);} else if (node.balance == 2) {if (getHeight(node.right.right) < getHeight(node.right.left)) {node.right = rotateRight(node.right);}node = rotateLeft(node);}if (null != node.parent) {rebalance(node.parent);} else {root = node;}}复制代码

左右旋

左旋和右旋的原理是一样的,只要了解一种即可。

    private Node rotateLeft(Node node) {Node right = node.right;right.parent = node.parent;node.right = right.left;if (null != node.right)node.right.parent = node;right.left = node;node.parent = right;if (null != right.parent) {if (right.parent.right == node) {right.parent.right = right;} else {right.parent.left = right;}}setBalance(node, right);return right;}复制代码
    private Node rotateRight(Node node) {Node left = node.left;left.parent = node.parent;node.left = left.right;if (null != node.left)node.left.parent = node;left.right = node;node.parent = left;if (null != left.parent) {if (left.parent.right == node) {left.parent.right = left;} else {left.parent.left = left;}}setBalance(node, left);return left;}复制代码

设置平衡点

首先是重置高度,取左右子树高度的最大值加一作为该节点的高度。

其次是设置平衡点,用来判断该节点是否平衡

    private void setBalance(Node... nodes) {for (Node node : nodes) {if (null != node) {//重置高度node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;node.balance = getHeight(node.right) - getHeight(node.left);}}}复制代码

获取树的高度

    private int getHeight(Node node) {if (null != node)return node.height;return -1;}复制代码

测试代码

这里打印用的中序遍历(中序遍历可以从小到大打印出来)

    private void print(Node node) {if (null != node) {print(node.left);System.out.println(node.data + " " + node.balance);print(node.right);}}public static void main(String[] args) {AVLTree avlTree = new AVLTree();for (int i = 1; i < 10; i++) {avlTree.insert(i);}avlTree.print();}复制代码

测试结果

4

2              6

1          3    5           8

7           9

data - balance

end

您的点赞和关注是对我最大的支持,谢谢!

关于二叉树的几个必须掌握的实现相关推荐

  1. 二叉树的前序、中序、后序非递归遍历 python实现

    前言 python中二叉树的定义: class TreeNode:def __init__(self, x):self.val = xself.left = Noneself.right = None ...

  2. 二叉树中和为某一值的路径

    前言 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.(注意: 在返回值的list中,数组长度大的数 ...

  3. 判断某数组是不是二叉树的前序遍历序列 python递归

    code class Solution:def VerifySquenceOfBST(self, sequence):# write code hereif len(sequence) <= 0 ...

  4. 翻转二叉树 c语言实现 递归 栈 队列

    前言 题目比较好理解,就是翻转二叉树 代码 c语言实现 #include<stdio.h> #include<stdlib.h> #include<string.h> ...

  5. python实现二叉树的重建2 之由中序遍历和后序遍历重建

    前言 通过上一节对python实现二叉树的重建1 之由前序遍历和中序遍历重建,我相信我们再来做这个问题就不难了,完全可以照猫画虎的来实现,具体的原理几乎是一样的,直接上代码了 code # 通用解法d ...

  6. 通过前序遍历和中序遍历构建二叉树 python实现

    前言 通过前序遍历和中序遍历构建二叉树的原理,主要是找前序遍历根节点在中序遍历中的位置,然后将二叉树而成左子树和右子树,然后依次进行这样的操作,思路还是比较简单的 代码 class Node:def ...

  7. 关于二叉树的层次遍历的花样(c++实现)

    花样变形1::二叉树层次遍历但是分层打印 分析:与普通打印多了一个分层打印,其实只要在在层次遍历中多设置一个标记变量即可 代码如下: //二叉树的层次遍历 void levelTravel(BTNod ...

  8. LeetCode简单题之二叉树的层平均值

    题目 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值.与实际答案相差 10-5 以内的答案可以被接受. 示例 1: 输入:root = [3,9,20,null,null ...

  9. LeetCode简单题之二叉树的最大深度

    题目 给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说明: 叶子节点是指没有子节点的节点. 示例: 给定二叉树 [3,9,20,null,null,15, ...

  10. LeetCode简单题之二叉树中第二小的节点

    题目 给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0.如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个. 更正式地说,即 root.val ...

最新文章

  1. Java解析JSON时,new JSONObject(这儿写json字符串)报错问题
  2. 博士申请 | 加拿大Mila实验室唐建教授招收深度学习方向博士生和实习生
  3. 回溯算法【0-1背包问题】
  4. 通过ap运行cab安装程序的方法及Sample Code
  5. 计算机软件在矿井地质中的应用,(完整版)遥感导论知识点整理(梅安新版)
  6. Nginx 禁止猜测路径上传恶意代码
  7. yamlcpp遍历_gf-cli 命令行工具
  8. ng6 常见错误汇总(持续更新)
  9. android 8 wifi 信号等级
  10. Redis搭建及介绍
  11. STM8S003F3 使用内置的Data EEPROM的操作接口
  12. python负数字符串转成_python 转字符
  13. 一键安装ROS和rosdep(NO 墙)
  14. AWS学习(一)——AWS云技术基础
  15. java 栅栏_Java并发包之闭锁/栅栏/信号量(转)
  16. qq空间的相册名称java_qq相册名称大全简单
  17. 【转】互操作性的区块链系统设计理念
  18. snmp中mib文件解析
  19. 一、 网络安全基础入门-概念名词
  20. 计算机系统结构复习(五):ILP指令集并行

热门文章

  1. Ovirt 安装部署方法
  2. Get/POST方法提交的长度限制
  3. get the better of sb
  4. 9月第1周安全回顾 IM安全威胁严重 企业增加无线安全投入
  5. idea下,Jetty采用main方法启动web项目
  6. ROS 用 roboware实现节点信息发送和接收
  7. Kafka系列三 java API操作
  8. 现在无法开始异步操作。异步操作只能在异步处理程序或模块中开始,或在页生存期中的特定事件过程中开始...
  9. cocos2d 从v1.x升级到v2.x需要注意的几个地方
  10. dropbear编译安装及服务脚本编写