本文目录

前两篇文章我们学习了一些树的基本概念以及常用操作,本篇我们了解一下二叉树的一种特殊形式:二叉排序树(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. 【已解决】C#中往SQLServer插入数据时遇到BUG

    错误信息如下: “System.Data.SqlClient.SqlException”类型的未经处理的异常在 System.Data.dll 中发生 其他信息: “”附近有语法错误. 文字版代码如下 ...

  2. JDK1.8的新特性

    JAVA8新特性 接口改善 现在接口里已经完全可以定义静态方法了. 举一个比较普遍的例子就是在java类库中, 对于一些接口如Foo, 都会有一个有静态方法的工具类Foos 来生成或者配合Foo对象实 ...

  3. mac下nginx安装

    一.安装 Nginx 终端执行: brew search nginx brew install nginx 当前版本 1.10.2,通过brew可以把nginx需要的pcre,openssl,zlib ...

  4. Linux下gcc和g++的区别

    首先编写了第一个C++程序,Hello,world! #include <iostream> using namespace std; void main() ...{ cout < ...

  5. Shiro入门之一 -------- Shiro权限认证与授权

    一  将Shirojar包导入web项目 二 在web.xml中配置shiro代理过滤器 注意: 该过滤器需要配置在struts2过滤器之前 <!-- 配置Shiro的代理过滤器 -->  ...

  6. Spring Cloud authentication with JWT service

    @RequestMapping(value = "/authenticate", method = RequestMethod.POST) public ResponseEntit ...

  7. cmder中文乱码、文字重叠等问题

    对于玩linux的同学来说,win7中的cmd弱爆了,今天我在微博中看到cmder这个工具,自己安装了,觉得不错,就此分享给大家 下载安装到这里http://bliker.github.io/cmde ...

  8. 玩转spring MVC(九)---Spring Data JPA

    偷个懒 在网上看有写的比较好的,直接贴个链接吧:http://***/forum/blogPost/list/7000.html 版权声明:本文为博主原创文章,未经博主允许不得转载.

  9. Jenkins持续集成项目搭建与实践——基于Python Selenium自动化测试(自由风格)

    Jenkins简介 Jenkins是Java编写的非常流行的持续集成(CI)服务,起源于Hudson项目.所以Jenkins和Hudson功能相似. Jenkins支持各种版本的控制工具,如CVS.S ...

  10. SQL2008全部数据导出导入两种方法

    方法一:生成脚本导出导入sql2008全部数据第一步,右键要导出的数据库,任务--生成脚本 第二步,在设置脚本编写选项处,点击--高级(A),选择要编写脚本的数据的类型为:架构和数据 如果找不到 要编 ...