本文目录

前两篇文章我们学习了一些树的基本概念以及常用操作,本篇我们了解一下二叉树的一种特殊形式:二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。

一、二叉排序树定义

二叉排序树或者是一颗空树,或者是具有下列性质的二叉树:

  • 若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值

  • 若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值

  • 它的左,右子树也分别为二叉排序树

也就是说二叉排序树中左子树结点值均小于根结点值,右子树节点值均大于跟节点值,左右子树同样满足上述约定。

如下图,即为一颗二叉排序树:

由二叉树定义知道,我们通过中序遍历二叉树就可以按照从小到大顺序排列二叉树中所有元素。

如上图中序遍历结果为:35, 40, 42, 45, 50, 67

二、java代码实现二叉排序树核心方法

下面我们通过java代码实现二叉排序树中的几个核心方法

我们先看下每个结点类定义如下:

 1class TreeNode{
2 private int data;
3 private TreeNode leftChild;
4 private TreeNode rightChild;
5 private TreeNode parent;
6
7 public TreeNode(int data) {
8 this.data = data;
9 this.leftChild = null;
10 this.rightChild = null;
11 this.parent = null;
12 }
13}

很简单,每个结点记录自己以及左右孩子,父类的信息。

二叉排序树的创建(增加元素方法)

创建一颗二叉排序树就是不断往里面添加元素。 
整体思路为:

  • 判断整棵树根结点是否创建过,如果没有创建那么第一个加入进来的元素指定为根结点,方法返回。

  • 如果二叉排序树已经创建过,那么再往里面加入元素需要先找出其父节点,然后将要插入的元素挂载到父节点下即可。

  • 经过上面过程找出其父结点,这里只需创建节点,挂载到父节点下即可,指定为父节点左孩子还是右孩子只需比较一下元素大小即可。

源码:

 1    public TreeNode put(int data){
2 TreeNode node = root;
3 TreeNode parent = null;
4 //判断二叉排序树根结点是否存在,不存在则创建
5 if (root == null){
6 root = new TreeNode(data);
7 return root;
8 }
9 //查找其父类
10 while (node != null){
11 parent = node;//记录其父亲节点
12 if (data > node.data){
13 node = node.rightChild;
14 }else if (data < node.data){
15 node = node.leftChild;
16 }else {
17 //已经存在则直接返回
18 return node;
19 }
20 }
21 //创建新节点并插入原有树中
22 node = new TreeNode(data);
23 if (data < parent.data){
24 parent.leftChild = node;
25 }else {
26 parent.rightChild = node;
27 }
28 node.parent = parent;
29 return node;
30 }
二叉排序树的查找

二叉排序树中查找比较简单,思路为:

  • 当前结点与查找的数据比较,相等则返回

  • 若小于当前结点则从左子树查找即可

  • 若大于当前结点则从右子树查找即可
    重复上述过程,这里就看出二分查找思想了

源码:

 1    public TreeNode searchNode(int data) {
2 TreeNode node = root;
3 if (node == null){
4 return null;
5 }else {
6 while (node != null && data != node.data){
7 if (data < node.data){
8 node = node.leftChild;
9 }else {
10 node = node.rightChild;
11 }
12 }
13 }
14 return node;
15 }
二叉排序树的删除

二叉排序树的删除操作分4中情况:

  • 若要删除的结点无左右孩子也就是叶子结点,那么直接删除即可,将其父节点左或者右孩子置null即可

  • 若要删除的结点有左孩子无右孩子,则只需要将删除结点的左孩子与其父节点建立关系即可

  • 若要删除的结点有右孩子无左孩子,则只需要将删除结点的右孩子与其父节点建立关系即可

  • 若要删除的结点左右孩子均有,就需要选一个结点将其替换,这里需要保证选取的结点保证比左子树都大,右子树都小,可以选取左子树中最大的结点,或者右子树中最小的结点,并且需要将选取的结点从二叉排序树中删除。

源码:这里我们选取右子树最小的结点

 1    public void deleteNode(int data){
2 TreeNode node = searchNode(data);
3 if (node == null){
4 throw new RuntimeException("未找到要删除的节点");
5 }else {
6 delete(node);
7 }
8 }
9
10 private void delete(TreeNode node) {
11 if (node == null){
12 throw new RuntimeException("未找到要删除的节点");
13 }else {
14 TreeNode parent = node.parent;
15 //删除的节点无左右孩子
16 if (node.leftChild == null && node.rightChild == null){
17 if (parent.leftChild == node){
18 parent.leftChild = null;
19 }else {
20 parent.rightChild = null;
21 }
22 return;
23 }
24 //删除的节点有左无右
25 if (node.leftChild != null
26 && node.rightChild == null){
27 if (parent.leftChild == node){
28 parent.leftChild = node.leftChild;
29 }else {
30 parent.rightChild = node.leftChild;
31 }
32 return;
33 }
34 //删除的节点有右无左
35 if (node.leftChild == null
36 && node.rightChild != null){
37 if (parent.leftChild == node){
38 parent.leftChild = node.rightChild;
39 }else {
40 parent.rightChild = node.rightChild;
41 }
42 return;
43 }
44 //删除的结点左右都有
45 TreeNode rightMinNode = getRightMinNode(node.rightChild);
46 delete(rightMinNode);
47 node.data = rightMinNode.data;
48 }
49 }
50
51 //获取右子树最小的结点
52 private TreeNode getRightMinNode(TreeNode node) {
53 TreeNode minNode = node;
54 while (minNode != null && minNode.leftChild != null){
55 minNode = minNode.leftChild;
56 }
57 System.out.println("minNode" + minNode.data);
58 return minNode;
59 }
接下来我们测试一下

测试代码:

 1 SearchBinaryTree ss = new SearchBinaryTree();
2 int[] array = {77,88,34,55,66,2,34,67,78};
3 for (int data : array) {
4 ss.put(data);
5 }
6 ss.midIter(ss.getRoot());
7 System.out.println();
8 SearchBinaryTree.TreeNode node = ss.searchNode(66);
9 System.out.println("find node:"+node.getData());
10 ss.deleteNode(66);
11 SearchBinaryTree.TreeNode dnode = ss.searchNode(66);
12 if (dnode != null){
13 System.out.println("find node:"+node.getData());
14 }else {
15 System.out.println("not find node");
16 }
17 ss.midIter(ss.getRoot());

打印信息如下:

1    2 34 55 66 67 77 78 88
2 find node:66
3 not find node
4 2 34 55 67 77 78 88

三、二叉排序树性能问题

二叉排序树最好的情况下其查找性能是很高的,接近二分查找法。 
但是在有些情况下构建出的二叉排序树类似一个链表,其查找性能为O(n),如下图:

构建出这样的树肯定不是我们希望的,需要调整此树达到平衡的效果,这里就需要二叉平衡树了(AVL树),关于AVL树会在后续篇章介绍,这里知道二叉平衡树有这个问题就可以了。

四、总结

本篇主要介绍了二叉平衡树以及Java代码实现其核心方法,希望你能掌握其与普通二叉树的区别,以及其存在的问题,好了,本片到此为止,希望对你有用。

声明:文章将会陆续搬迁到个人公众号,以后也会第一时间发布到个人公众号,及时获取文章内容请关注公众号

最后附上整个类的全部源码,拷贝过去就可以用了:

  1    public class SearchBinaryTree {
2
3 private TreeNode root;//二叉树根结点
4
5 public TreeNode getRoot() {
6 return root;
7 }
8
9 //中序遍历二叉排序树:按照从小到大排序
10 public void midIter(TreeNode node){
11 if (node == null){
12 return;
13 }
14 midIter(node.leftChild);
15 System.out.print(" "+node.data);
16 midIter(node.rightChild);
17 }
18
19 public TreeNode put(int data){
20 TreeNode node = root;
21 TreeNode parent = null;
22 //判断二叉排序树根结点是否存在,不存在则创建
23 if (root == null){
24 root = new TreeNode(data);
25 return root;
26 }
27 //查找其父类
28 while (node != null){
29 parent = node;//记录其父亲节点
30 if (data > node.data){
31 node = node.rightChild;
32 }else if (data < node.data){
33 node = node.leftChild;
34 }else {
35 //已经存在则直接返回
36 return node;
37 }
38 }
39 //创建新节点并插入原有树中
40 node = new TreeNode(data);
41 if (data < parent.data){
42 parent.leftChild = node;
43 }else {
44 parent.rightChild = node;
45 }
46 node.parent = parent;
47 return node;
48 }
49
50 public void deleteNode(int data){
51 TreeNode node = searchNode(data);
52 if (node == null){
53 throw new RuntimeException("未找到要删除的节点");
54 }else {
55 delete(node);
56 }
57 }
58
59 private void delete(TreeNode node) {
60 if (node == null){
61 throw new RuntimeException("未找到要删除的节点");
62 }else {
63 TreeNode parent = node.parent;
64 //删除的节点无左右孩子
65 if (node.leftChild == null && node.rightChild == null){
66 if (parent.leftChild == node){
67 parent.leftChild = null;
68 }else {
69 parent.rightChild = null;
70 }
71 return;
72 }
73 //删除的节点有左无右
74 if (node.leftChild != null
75 && node.rightChild == null){
76 if (parent.leftChild == node){
77 parent.leftChild = node.leftChild;
78 }else {
79 parent.rightChild = node.leftChild;
80 }
81 return;
82 }
83 //删除的节点有右无左
84 if (node.leftChild == null
85 && node.rightChild != null){
86 if (parent.leftChild == node){
87 parent.leftChild = node.rightChild;
88 }else {
89 parent.rightChild = node.rightChild;
90 }
91 return;
92 }
93 //删除的结点左右都有
94 TreeNode rightMinNode = getRightMinNode(node.rightChild);
95 delete(rightMinNode);
96 node.data = rightMinNode.data;
97 }
98 }
99
100 private TreeNode getRightMinNode(TreeNode node) {
101 TreeNode minNode = node;
102 while (minNode != null && minNode.leftChild != null){
103 minNode = minNode.leftChild;
104 }
105 System.out.println("minNode" + minNode.data);
106 return minNode;
107 }
108
109 public TreeNode searchNode(int data) {
110 TreeNode node = root;
111 if (node == null){
112 return null;
113 }else {
114 while (node != null && data != node.data){
115 if (data < node.data){
116 node = node.leftChild;
117 }else {
118 node = node.rightChild;
119 }
120 }
121 }
122 return node;
123 }
124
125
126 public class TreeNode{
127 private int data;
128 private TreeNode leftChild;
129 private TreeNode rightChild;
130 private TreeNode parent;
131
132 public TreeNode(int data) {
133 this.data = data;
134 this.leftChild = null;
135 this.rightChild = null;
136 this.parent = null;
137 }
138
139 public int getData() {
140 return data;
141 }
142
143 public void setData(int data) {
144 this.data = data;
145 }
146
147 public TreeNode getLeftChild() {
148 return leftChild;
149 }
150
151 public void setLeftChild(TreeNode leftChild) {
152 this.leftChild = leftChild;
153 }
154
155 public TreeNode getRightChild() {
156 return rightChild;
157 }
158
159 public void setRightChild(TreeNode rightChild) {
160 this.rightChild = rightChild;
161 }
162
163 public TreeNode getParent() {
164 return parent;
165 }
166
167 public void setParent(TreeNode parent) {
168 this.parent = parent;
169 }
170 }
171}

Android版数据结构与算法(八):二叉排序树的更多相关文章

  1. Android版数据结构与算法(七):赫夫曼树

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 近期忙着新版本的开发,此外正在回顾C语言,大部分时间没放在数据结构与算法的整理上,所以更新有点慢了,不过既然写了就肯定尽力将这部分完全整理好分享出 ...

  2. Android版数据结构与算法(一):基础简介

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 一.前言 项目进入收尾阶段,忙忙碌碌将近一个多月吧,还好,不算太难,就是麻烦点. 数据结构与算法这个系列早就想写了,一是梳理总结,顺便逼迫自己把一 ...

  3. Android版数据结构与算法(六):树与二叉树

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 之前的篇章主要讲解了数据结构中的线性结构,所谓线性结构就是数据与数据之间是一对一的关系,接下来我们就要进入非线性结构的世界了,主要是树与图,好了接 ...

  4. Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 上一篇基于哈希表实现HashMap核心源码彻底分析 分析了HashMap的源码,主要分析了扩容机制,如果感兴趣的可以去看看,扩容机制那几行最难懂的 ...

  5. Android版数据结构与算法(四):基于哈希表实现HashMap核心源码彻底分析

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 存储键值对我们首先想到HashMap,它的底层基于哈希表,采用数组存储数据,使用链表来解决哈希碰撞,它是线程不安全的,并且存储的key只能有一个为 ...

  6. Android版数据结构与算法(三):基于链表的实现LinkedList源码彻底分析

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. LinkedList 是一个双向链表.它可以被当作堆栈.队列或双端队列进行操作.LinkedList相对于ArrayList来说,添加,删除元素效 ...

  7. Android版数据结构与算法(二):基于数组的实现ArrayList源码彻底分析

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 本片我们分析基础数组的实现--ArrayList,不会分析整个集合的继承体系,这不是本系列文章重点. 源码分析都是基于"安卓版" ...

  8. JavaScript 版数据结构与算法(二)队列

    今天,我们要讲的是数据结构与算法中的队列. 队列简介 队列是什么?队列是一种先进先出(FIFO)的数据结构.队列有什么用呢?队列通常用来描述算法或生活中的一些先进先出的场景,比如: 在图的广度优先遍历 ...

  9. JavaScript 版数据结构与算法(一)栈

    今天,我们要讲的是数据结构与算法中的栈. 栈的简介 栈是什么?栈是一个后进先出(LIFO)的数据结构.栈有啥作用?栈可以模拟算法或生活中的一些后进先出的场景,比如: 十进制转二进制,你需要将余数倒序输 ...

随机推荐

  1. AUTOSAR ArcticCore重构 - for_each_HOH

    Arctic Core是AUTOSAR的实现,早期版本是开源的. 基本问题 在ARM架构下对CAN driver的实现(arch/arm/arm_cm3/drivers/Can.c)中,有这样一段代码 ...

  2. api大全

    免费api大全(更新中) API大全  http://www.apidq.com/    (这个碉堡了) 天气接口 气象局接口 完整数据:http://m.weather.com.cn/data/10 ...

  3. SSM博客登录注册

    我的博客采用的是 spring+springmvc+mybatis框架,用maven和git管理项目,之后的其他功能还有待进一步的学习. 首先新建一个maven项目,我的项目组成大概就这样, 建立好项 ...

  4. Maven安装和使用

    一.安   装 1.解压好后,添加系统环境变量 变量名:MAVEN_HOME 属性值:D:\apache-maven-3.3.3  //也就是解压的路径 path中添加:%MAVEN_HOME%\bi ...

  5. vue国际化高逼格多语言

    ## 1.NPM 项目安装 ``` cnpm i vue-i18n ``` ## 2.使用方法 ``` /* 国际化使用规则 */ import Vue from 'vue' import VueI1 ...

  6. linux内核裁剪及编译可加载模块

    一:linux内核裁剪: 1:编译内核源码: 今天的重点内容是内核驱动的编写,在编写驱动之前首先的了解linux内核源码,linux主要是由五个子系统组成:进程调度,内存管理,文件系统,网络接口以及进 ...

  7. Java中 float、double使用注意问题

    在java中运行一下代码 System.out.println(2.00-1.10);输出的结果是:0.8999999999999999很奇怪,并不是我们想要的值0.9 再运行如下代码:System. ...

  8. (WCF初体验)WCF的认证和消息保护

    最近做WCF开发,有个需求是在服务端做认证,网上查资料了解到可以用UserName和Password 来做认证,只需要写好配置文件和在服务端写好验证类就行了,但是网上普遍的博文都是需要用证书,而我自己 ...

  9. Linux时间子系统之(二):软件架构

    专题文档汇总目录 Notes:从框架上讲解了时间子系统,从底向上包括CPU Local TImer.Global Counter.Clock Souce/Clock Events模块管理.Tick D ...

  10. RedHat 7.0及CentOS 7.0禁止Ping的三种方法

    作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=538 前言: "Ping"属于ICMP协议(即"Internet控制报文协议") ...