Android版数据结构与算法(八):二叉排序树
本文目录
前两篇文章我们学习了一些树的基本概念以及常用操作,本篇我们了解一下二叉树的一种特殊形式:二叉排序树(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版数据结构与算法(八):二叉排序树的更多相关文章
- Android版数据结构与算法(七):赫夫曼树
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 近期忙着新版本的开发,此外正在回顾C语言,大部分时间没放在数据结构与算法的整理上,所以更新有点慢了,不过既然写了就肯定尽力将这部分完全整理好分享出 ...
- Android版数据结构与算法(一):基础简介
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 一.前言 项目进入收尾阶段,忙忙碌碌将近一个多月吧,还好,不算太难,就是麻烦点. 数据结构与算法这个系列早就想写了,一是梳理总结,顺便逼迫自己把一 ...
- Android版数据结构与算法(六):树与二叉树
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 之前的篇章主要讲解了数据结构中的线性结构,所谓线性结构就是数据与数据之间是一对一的关系,接下来我们就要进入非线性结构的世界了,主要是树与图,好了接 ...
- Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 上一篇基于哈希表实现HashMap核心源码彻底分析 分析了HashMap的源码,主要分析了扩容机制,如果感兴趣的可以去看看,扩容机制那几行最难懂的 ...
- Android版数据结构与算法(四):基于哈希表实现HashMap核心源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 存储键值对我们首先想到HashMap,它的底层基于哈希表,采用数组存储数据,使用链表来解决哈希碰撞,它是线程不安全的,并且存储的key只能有一个为 ...
- Android版数据结构与算法(三):基于链表的实现LinkedList源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. LinkedList 是一个双向链表.它可以被当作堆栈.队列或双端队列进行操作.LinkedList相对于ArrayList来说,添加,删除元素效 ...
- Android版数据结构与算法(二):基于数组的实现ArrayList源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 本片我们分析基础数组的实现--ArrayList,不会分析整个集合的继承体系,这不是本系列文章重点. 源码分析都是基于"安卓版" ...
- JavaScript 版数据结构与算法(二)队列
今天,我们要讲的是数据结构与算法中的队列. 队列简介 队列是什么?队列是一种先进先出(FIFO)的数据结构.队列有什么用呢?队列通常用来描述算法或生活中的一些先进先出的场景,比如: 在图的广度优先遍历 ...
- JavaScript 版数据结构与算法(一)栈
今天,我们要讲的是数据结构与算法中的栈. 栈的简介 栈是什么?栈是一个后进先出(LIFO)的数据结构.栈有啥作用?栈可以模拟算法或生活中的一些后进先出的场景,比如: 十进制转二进制,你需要将余数倒序输 ...
随机推荐
- vue config.js配置生产环境和发布环境不同的接口地址问题
第一步,分别设置不同的接口地址 首先,我们分别找到下面的文件: /config/dev.env.js /config/prod.env.js 其实,这两个文件就是针对生产环境和发布环境设置不同参数的文 ...
- .NET开发微信小程序-获取OpenId
注:获取当前用户信息只需要用GetUserInfo这个方法就行.这里就不需要提了 前端代码: CallBack:回调函数 function GetOpenID(CallBack){ var appIn ...
- File文件的读写操作RandomAccessFile类
1.Java提供了一个对文件随机访问的操作,访问包括读和写操作,该类名是RandomAccessFile,该类的读写是基于指针的操作. 2.RandomAccessFile在堆文件进行随机访问操作时有 ...
- iOS 中判断应用程序是否为第一次打开
第一步:在AppDelegate中当应用启动完成后加入一下代码: - (BOOL)application:(UIApplication *)application didFinishLaunching ...
- ZooKeeper的安装
一.准备 需要提前安装好Java 准备好zookeeper的软件包:软件包地址 二.部署 解压zookeeper压缩包到指定目录 执行如下命令: .tar.gz -C /opt/ 三.修改配置 ...
- (WCF初体验)WCF服务器诊断
WCF服务器搭建好之后,不管是客户端访问还是本地调试,出个问题抛出来的原因往往在我们看来都是不知所以然的,更可能是跑出来的问题和真正的问题差了很远,比如"通信对象 System.Servic ...
- 解决Ubuntu系统下的VMware Workstation无法打开虚拟网络编辑器界面的问题
本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=630 操作环境: Ubuntu 17 VMware 14 pro for Linux 问题描述: 我在Ubuntu ...
- Flask开发微电影网站(二)
1.安装数据库连接依赖包 pip install flask-sqlalchemy 2.创建movie数据库 在CentOS虚拟机,进入MaridDB数据库提示符,创建movie数据库 create ...
- Securing Spring Cloud Microservices With OAuth2
From Zero to OAuth2 in Spring cloud Today I am presenting hours of research about a (apparently) sim ...
- WinForm时间选择控件(DateTimePicker)如何选择(显示)时分秒
C# Windows窗体应用中,用到时间选择控件DateTimePicker,发现不能选择时分秒,难道要自己写一个控件?! 答案是否定的,通过属性修改是可以选择时间的,DateTimePicker完全 ...