为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨。

  首先,二叉搜索树是啥?它有什么用呢?

  二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指针,1个左孩子指针,1个有孩子指针,还有就是自己的数据部分了,因为只有左右两孩子,所以才叫二叉树,在此基础上,该二叉树还满足另外一个条件:每个结点的左孩子都不大于该结点&&每个结点的右孩子都大于该结点。这样,我们队这棵树进行中序遍历,就能把key从小到大排序了……

  那么问题来了,我都有线性表有链表了,我还要它干啥?两个字!效率!

  相比线性表,你要搜索一个key,就要执行一次线性时间,算法复杂度为O(n);而用二叉搜索树,算法效率是O(lgn)!这是很诱人的数字。下面我用Java实现以下二叉搜索树,你自然就明白为什么算法复杂度是O(lgn)了。

  其次,写一个数据结构,自然而然也要实现对这个数据结构的增、删、查、改了。

  下面是我的思路:

  1. 创建树:我是通过一个一个结点的插入来建立一棵二叉搜索树。
  2. 搜索结点:从根节点开始,进行key的比较,小了就往左走,大了就往右走,最后到了叶子结点都还没有的话,那么该树就不存在要搜索的结点了。
  3. 修改结点:修改其实就是查询,在查询之后把结点的数据部分给改了而已,这里我就不重复去实现了。
  4. 删除结点:这个应该就是最难的了,所以我有必要详细讲,先上图(不好意思,懒得用软件画图了,将就将就下哈):
当我们要删除一个结点时,分如下几种情况:
  • 此结点是叶子结点,这个最简单啦,直接把结点给释放掉就行了。(如图删除9)
  • 此结点只有左孩子,这个也简单啦,直接把左子树替换过来就行了。(如图删除3)
  • 此结点只有右孩子,同上。(如图删除8)
  • 此结点有左右孩子,当出现这种情况时(如图删除7),我们就要找出该结点的后继结点(因为右子树肯定存在,所以找肯定在右子树中),然后把这个后继结点替换到要删除的结点中,然后继续执行对这个后继结点的删除操作(递归删除操作就行了)。
 
  发现没?现在我的解题思路是自顶向下去分析……自顶向下,逐级求精是一个很伟大的思想!
 
  现在问题来了!后继结点怎么求?我们来分析一下,当求一个结点的后继结点时,分为以下两种情况:
  • 当该结点有右孩子时,后继结点就在右子树中,就是该右子树的最小结点
  • 当该结点没有右孩子时,那后继结点就满足这个条件:该后继结点是该结点的祖先&&该结点位于该结点的左子树中(如图中的9的后继结点是12)
  哎呀呀!问题又来了!最小结点咋办!很简单!
  当求一棵树的最小结点时,那么就要从这颗树的根节点开始,一直往左子树走,就能找到它的最小结点了!
  好了,现在问题逐步解决了!删除结点的功能也就完成了!
  最后,没代码说个锤子,咱上代码!
 
首先,写个测试类:
  1. public class Test {
  2. public static void main(String[] args) {
  3. int[] datas={12,4,5,7,4,8,3,2,6,9};
  4. BinTree tree=new BinTree(datas);
  5. tree.preOrderTraverse();//先序遍历
  6. tree.midOrderTraverse();//中序遍历
  7. tree.postOrderTraverse();//后序遍历
  8. tree.insert(15); //插入结点
  9. tree.search(7); //查询结点
  10. tree.search(100); //查询一个不存在的结点
  11. tree.getMax(); //获取最大值
  12. tree.getMin(); //获取最小值
  13. tree.getPre(7); //前驱结点
  14. tree.getPre(2); //最前的前驱结点
  15. tree.getPost(7); //后继结点
  16. tree.getPost(15); //最后的后继结点
  17. tree.delete(5); //删除结点
  18. tree.delete(0); //删除一个不存在的结点
  19. }
  20. }
然后,二叉搜索树:
  1. public class BinTree {
  2. Node root=null;
  3. private class Node{
  4. Node parent=null;
  5. Node leftChild=null;
  6. Node rightChild=null;
  7. int key;
  8. public Node(int data) {
  9. this.key=data;
  10. }
  11. }
  12. public BinTree(int[] datas) {
  13. buildTree(datas);
  14. }
  15. private void buildTree(int[] datas) {
  16. for (int i = 0; i < datas.length; i++) {
  17. Node node=new Node(datas[i]);
  18. insertNode(node);
  19. }
  20. }
  21. private void insertNode(Node node) { //插入结点
  22. Node next=this.root;
  23. Node cur=null; //用来保存当前结点
  24. while(next!=null){ //当到达叶子结点时,确认位置!
  25. cur=next;
  26. if(node.key>=cur.key){
  27. next=next.rightChild;
  28. }else{
  29. next=next.leftChild;
  30. }
  31. }
  32. node.parent=cur; //插入该结点!
  33. if(cur==null){
  34. this.root=node; //该树为空树,所以这个是根节点
  35. }else if(node.key>=cur.key){
  36. cur.rightChild=node;
  37. }else{
  38. cur.leftChild=node;
  39. }
  40. }
  41. /*
  42. * 插入一个数
  43. */
  44. public void insert(int data){
  45. Node node=new Node(data);
  46. System.out.println("插入结点:"+data);
  47. insertNode(node);
  48. this.midOrderTraverse();
  49. }
  50.  
  51. /*
  52. * 先序遍历
  53. */
  54. public void preOrderTraverse(){
  55. System.out.println("先序遍历:");
  56. preOrderTraverse(root);
  57. System.out.println();
  58. }
  59. private void preOrderTraverse(Node node){ //先序遍历
  60. if(node!=null){
  61. System.out.print("-"+node.key+"-");
  62. preOrderTraverse(node.leftChild);
  63. preOrderTraverse(node.rightChild);
  64. }
  65. }
  66. /*
  67. * 中序遍历
  68. */
  69. public void midOrderTraverse(){
  70. System.out.println("中序遍历:");
  71. midOrderTraverse(root);
  72. System.out.println();
  73. }
  74. private void midOrderTraverse(Node node){ //中序遍历
  75. if(node!=null){
  76. midOrderTraverse(node.leftChild);
  77. System.out.print("-"+node.key+"-");
  78. midOrderTraverse(node.rightChild);
  79. }
  80.  
  81. }
  82.  
  83. /*
  84. * 后序遍历
  85. */
  86. public void postOrderTraverse(){
  87. System.out.println("后序遍历:");
  88. postOrderTraverse(root);
  89. System.out.println();
  90. }
  91. private void postOrderTraverse(Node node){ //后序遍历
  92. if(node!=null){
  93. System.out.print("-"+node.key+"-");
  94. postOrderTraverse(node.leftChild);
  95. postOrderTraverse(node.rightChild);
  96. }
  97. }
  98.  
  99. /*
  100. * 搜索结点
  101. */
  102. public void search(int data){
  103. System.out.println("您要查找的是:"+data);
  104. Node node;
  105. if((node=searchNode(new Node(data)))==null){
  106. System.out.println("树中没有该结点!");
  107. }else{
  108. System.out.println("查找"+node.key+"成功!");
  109. }
  110. }
  111.  
  112. private Node searchNode(Node node){ //private供内部调用,搜索结点
  113. if(node==null){
  114. System.out.println("输入为空,查找失败!");
  115. }else{
  116. if(root==null){
  117. System.out.println("该树为空树!");
  118. }else{ //开始查找
  119. boolean isFound=false;
  120. Node x=root;
  121. Node y=null;
  122. while(!isFound&&x!=null){ //当查到或者到了叶子节点还没查到时,终结!
  123. y=x;
  124. if(node.key==x.key){
  125. isFound=true;
  126. }else{ //通过比较大小往下面查找
  127. if(node.key>x.key){
  128. x=x.rightChild;
  129. }else{
  130. x=x.leftChild;
  131. }
  132. }
  133. }
  134. if(isFound){ //没找到的话,在最后返回null
  135. return y;
  136. }
  137. }
  138. }
  139. return null;
  140. }
  141.  
  142. /*
  143. * 获取最大值
  144. */
  145. public void getMax(){
  146. Node node;
  147. if((node=getMaxNode(root))==null){
  148. System.out.println("该树为空!");
  149. }else{
  150. System.out.println("最大的结点是:"+node.key);
  151. }
  152.  
  153. }
  154.  
  155. private Node getMaxNode(Node node){ //获取最大值
  156. if(node!=null){
  157. Node x=node;
  158. Node y=null;
  159. while(x!=null){ //一直往右遍历直到底就是最大值了!
  160. y=x;
  161. x=x.rightChild;
  162. }
  163. return y;
  164. }
  165. return null;
  166. }
  167.  
  168. /*
  169. * 获取最小值
  170. */
  171. public void getMin(){
  172. Node node;
  173. if((node=getMinNode(root))==null){
  174. System.out.println("该树为空!");
  175. }else{
  176. System.out.println("最小的结点是:"+node.key);
  177. }
  178. }
  179. private Node getMinNode(Node node){ //获取最小值
  180. if(node!=null){
  181. Node x=node;
  182. Node y=null;
  183. while(x!=null){ //一直往左遍历直到底就是最小值了!
  184. y=x;
  185. x=x.leftChild;
  186. }
  187. return y;
  188. }
  189. return null;
  190. }
  191.  
  192. /*
  193. * 获取前驱结点
  194. */
  195. public void getPre(int data){
  196. Node node=null;
  197. System.out.println(data+"的前驱结点:");
  198. if((node=getPreNode(searchNode(new Node(data))))==null){
  199. System.out.println("该结点不存在或无前驱结点!");
  200. }else{
  201. System.out.println(data+"的前驱结点为:"+node.key);
  202. }
  203. }
  204.  
  205. private Node getPreNode(Node node){ //获取前驱结点
  206. if(node==null){
  207. return null;
  208. }
  209. if(node.leftChild!=null){ //当有左孩子时,前驱结点就是左子树的最大值
  210. return getMaxNode(node.leftChild);
  211. }else{//当不存在左孩子时,前驱结点就是——它的祖先,而且,它在这个祖先的右子树中。这句话自己画图就能理解了
  212. Node x=node;
  213. Node y=node.parent;
  214. while(y!=null&&x==y.leftChild){
  215. x=y;
  216. y=y.parent;
  217. }
  218. return y;
  219. }
  220. }
  221.  
  222. /*
  223. * 获取后继结点
  224. */
  225. public void getPost(int data){
  226. Node node=null;
  227. System.out.println(data+"的后继结点:");
  228. if((node=getPostNode(searchNode(new Node(data))))==null){
  229. System.out.println("该结点不存在或无后继结点!");
  230. }else{
  231. System.out.println(data+"的后继结点为:"+node.key);
  232. }
  233. }
  234.  
  235. private Node getPostNode(Node node){ //获取后继结点
  236. if(node==null){
  237. return null;
  238. }
  239. if(node.rightChild!=null){ //当有右孩子时,前驱结点就是右子树的最小值
  240. return getMinNode(node.rightChild);
  241. }else{//当不存在右孩子时,后继结点就是——它的祖先,而且,它在这个祖先的左子树中。这句话自己画图就能理解了
  242. Node x=node;
  243. Node y=node.parent;
  244. while(y!=null&&x==y.rightChild){
  245. x=y;
  246. y=y.parent;
  247. }
  248. return y;
  249. }
  250. }
  251.  
  252. /*
  253. * 删除结点
  254. */
  255. public void delete(int data){
  256. Node node;
  257. if((node=searchNode(new Node(data)))==null){//注意!这里不能new结点!你必须从树中找该结点!new就是初始化了
  258. System.out.println("二叉树中不存在此结点!");
  259. return;
  260. }
  261. deleteNode(node);
  262. System.out.println("删除结点"+data+"后:");
  263. this.midOrderTraverse();
  264. }
  265.  
  266. private void deleteNode(Node node){
  267. if(node==null){
  268. System.out.println("删除结点不能为空!");
  269. return;
  270. }
  271. replacedNode(node);
  272. }
  273.  
  274. private void replacedNode(Node node) { //替换结点
  275. if(node.leftChild!=null
  276. &&node.rightChild!=null){ //当有左右孩子时,用后继结点替换
  277. replacedNodeOfPost(node);
  278. }
  279. else
  280. {
  281. if(node.leftChild!=null){ //当只有左孩子时,直接用左子树替换
  282. node=node.leftChild;
  283. }else if(node.rightChild!=null){ //只有右孩子时,直接有子树替换
  284. node=node.rightChild;
  285. }else{ //当没有左右孩子时,就直接释放了这个结点
  286. freeNode(node);
  287. }
  288. }
  289. }
  290.  
  291. private void freeNode(Node node) { //释放该结点,断掉其与父结点的链接
  292. if(node==node.parent.leftChild){
  293. node.parent.leftChild=null;
  294. }else{
  295. node.parent.rightChild=null;
  296. }
  297. }
  298.  
  299. private void replacedNodeOfPost(Node node) {
  300. Node y=this.getPostNode(node); //找后继结点
  301. node.key=y.key;
  302. replacedNode(y); //替换了key之后,再一次递归把现在这个结点给替换了!
  303. }
  304.  
  305. }

最后是测试结果:

------------------分割线-------------------------
先序遍历:
-12--4--3--2--5--4--7--6--8--9-
中序遍历:
-2--3--4--4--5--6--7--8--9--12-
后序遍历:
-12--4--3--2--5--4--7--6--8--9-
插入结点:15
中序遍历:
-2--3--4--4--5--6--7--8--9--12--15-
您要查找的是:7
查找7成功!
您要查找的是:100
树中没有该结点!
最大的结点是:15
最小的结点是:2
7的前驱结点:
7的前驱结点为:6
2的前驱结点:
该结点不存在或无前驱结点!
7的后继结点:
7的后继结点为:8
15的后继结点:
该结点不存在或无后继结点!
删除结点5后:
中序遍历:
-2--3--4--4--6--7--8--9--12--15-
二叉树中不存在此结点!
 

【算法与数据结构】二叉搜索树的Java实现的更多相关文章

  1. Java与算法之(13) - 二叉搜索树

    查找是指在一批记录中找出满足指定条件的某一记录的过程,例如在数组{ 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }中查找数字15,实现代码很简单 ...

  2. Java数据结构——二叉搜索树

    定义二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若 ...

  3. 数据结构-二叉搜索树和二叉树排序算法(python实现)

    今天我们要介绍的是一种特殊的二叉树--二叉搜索树,同时我们也会讲到一种排序算法--二叉树排序算法.这两者之间有什么联系呢,我们一起来看一下吧. 开始之前呢,我们先来介绍一下如何创建一颗二叉搜索树. 假 ...

  4. 二叉搜索树 思想 JAVA实现

    二叉搜索树:一棵二叉搜索树是以一棵二叉树来组织的,这样一棵树可以使用链表的数据结构来表示(也可以采用数组来实现).除了key和可能带有的其他数据外,每个节点还包含Left,Right,Parent,它 ...

  5. 数据结构-二叉搜索树(BST binary search tree)

    本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 二叉搜索树简介 顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来 ...

  6. 数据结构-二叉搜索树的js实现

    一.树的相关概念 1.基本概念 子树 一个子树由一个节点和它的后代构成. 节点的度 节点所拥有的子树的个数. 树的度 树中各节点度的最大值 节点的深度 节点的深度等于祖先节点的数量 树的高度 树的高度 ...

  7. 数据结构☞二叉搜索树BST

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它可以是一棵空树,也可以是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它 ...

  8. 二叉搜索树的java实现

    转载请注明出处 一.概念 二叉搜索树也成二叉排序树,它有这么一个特点,某个节点,若其有两个子节点,则一定满足,左子节点值一定小于该节点值,右子节点值一定大于该节点值,对于非基本类型的比较,可以实现Co ...

  9. 96题--不同的二叉搜索树(java、中等难度)

    题目描述:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 示例如下: 分析:本题可用动态规划的方法求解. 设 dp[n] 表示以 1 ... n 为节点组成的二叉搜索树的种类 ...

随机推荐

  1. MySql的连接查询

    类似于oracle的连接查询,mysql连接查询也有左外连接.右外连接.内连接查询.但是,不同的是没有直接 的全外连接查询. 这里介绍MySql的连接查询: 这里已两张表为例:STUDENT 表 和 ...

  2. CSS overflow 属性

     值 描述 visible 默认值.内容不会被修剪,会呈现在元素框之外. hidden 内容会被修剪,并且其余内容是不可见的. scroll 内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容. ...

  3. 如何在cmd下切换不同版本的Python

    (1)分别安装python-2.7.12.amd64.msipython-3.5.2-amd64.exe(python官网下载的)顺序无所谓(为了看着方便,我把安装路径修改统一了)(2)配置环境变量D ...

  4. 转 夕甲甲:孔乙己之 C++ 版

    欧欧匹代码的格局,是和别的编程模式不同的:首先要有一个构造函数:基类里只定义了函数的形式,可以随时通过派生增加不同的实现.那些程序员们,每每学会了继承和多态,便可以接一个项目,——这是十年前的事,现在 ...

  5. Android笔记:如何在Fragment里使用findViewById()方法?

    在Activity中,可以直接用findViewById(int id),通过xml的id来找到对应的View. 查找官方的api,具体如下: https://developer.android.go ...

  6. ConfigHelper.cs

    using System.Configuration; using System.IO; /// <summary> /// 配置文件辅助类 /// </summary> pu ...

  7. .net妹纸转Java---java环境的搭建,myeclipse10.0 的安装环境变量配置和破解

    啦啦啦 ,因为公司项目需要,从我大火炉--大武汉被拖到了更大的火炉--大广西  其实一开始 我的内心是拒绝的. 但是我在大武汉呆了近2年木有出过远门,对, 生活除了眼前的苟且,还有远方的苟且.怀揣这样 ...

  8. 04 Apache Solr: 目录结构

         通过前面的介绍,我们已经能够看到管理员界面并对Solr形成了一个感性的认识.本篇将在物理上深入了解Solr的安装目录结构和Solr示例的主目录结构.   安装目录结构 Solr 6.3.0安 ...

  9. github 添加项目

    下载git安装 ->https://git-scm.com/downloads 新建git目录 在目录下右键选择Git Bash Here 执行 git init 拷贝项目到git目录下 在gi ...

  10. spring boot 学习笔记(二) 构建web支持jsp

    一.必须将项目打包成war包 <packaging>war</packaging> 二.pom.xml加入依赖包 <dependency> <groupId& ...