本文目录

前两篇文章我们学习了一些树的基本概念以及常用操作,本篇我们了解一下二叉树的一种特殊形式:二叉排序树(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. 一些遇到的Qt程序在Windows平台间移植问题整理

    今天尝试把Qt程序移植到各种虚拟机中测试,由于Qt的依赖库报告往往不能显示出全部依赖库.结果频频出现问题,好不容易全部解决了,这里给出一些套路. 首先对于Qt版本,我用过很多,最终表示现阶段推荐Min ...

  2. webpack4 splitChunksPlugin && runtimeChunkPlugin 配置杂记

    webpack2 还没研究好,就发现升级到4了,你咋这么快~ 最近要做项目脚手架,项目构建准备重新做,因为之前写的太烂了...然后发现webpack大版本已经升到4了(又去看了一眼,4.5了),这么快 ...

  3. 【ASP.NET Core】JSON Patch 使用简述

    JSON Patch 是啥玩意儿?不知道,直接翻译吧,就叫它“Json 补丁”吧.干吗用的呢?当然是用来修改 JSON 文档的了.那咋修改呢?比较常见有四大操作:AMRR. 咋解释呢? A—— Add ...

  4. 网站开发中使用javascript获取浏览器滚动条宽度

    在网站开发中,有时候需要获取浏览器滚动条的宽度,在武汉蚂蹄软件服务中心的技术人员指导之下,我实现了该需求.记录如下: 首先说明一下原理: ①生成一个div,设置滚动条不可见,记录其宽度: ②将上面的d ...

  5. python3 爬去QQ音乐

    import requests import re import json import os def get_name(singer): url = 'https://c.y.qq.com/soso ...

  6. 爬虫值requests库

    requests简介 简介 Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库 ,使用起来比urllib简洁很多 因为是第三方库, ...

  7. Oracle12c中PL/SQL(DBMS_SQL)新特性之隐式语句结果(DBMS_SQL.RETURN_RESULT and DBMS_SQL.GET_NEXT_RESULT)

    隐式数据结果特性将能简化从其他数据库到Oracle12c存储过程迁移.1. 背景T-SQL中允许查询结果的隐式返回.例如:下面T-SQL存储过程隐式返回查询结果.CREATE PROCEDURE Ge ...

  8. HTTP多线程下载+断点续传(libcurl库)

    目录索引: 一.LibCurl基本编程框架 二.一些基本的函数 三.curl_easy_setopt函数部分选项介绍 四.curl_easy_perform 函数说明(error 状态码) 五.lib ...

  9. linux Tcpdump使用方法

    用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的&qu ...

  10. Python高级教程

    关键字is 和 == 的区别 a = 'hello world' b = 'hello world' a == b #返回True a is b #返回False 注意:is 判断是否是一个ID, = ...