转载 http://www.cnblogs.com/CherishFX/p/4625382.html

二叉查找树的定义:

  二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树:

    1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字;

    2. 若右子树非空,则右子树上所有节点关键字值均大于根节点的关键字;

    3. 左、右子树本身也分别是一颗二叉查找树。

二叉查找树的实现,功能有:

  1. 用一个数组去构建二叉查找树

  2. 二叉查找树的中序遍历和层次遍历

  3. 插入节点

  4. 查找节点

5. 查找二叉树中的最大值和最小值

  6.  得到节点的直接父节点

  7. 得到节点的直接前驱和直接后继节点

  8. 删除节点

  1. package dataStructures;
  2.  
  3. import java.util.LinkedList;
  4. import java.util.Queue;
  5. import java.util.Scanner;
  6. import java.util.Stack;
  7.  
  8. public class BinarySearchTree {
  9. private TreeNode<Integer> root = null; // 根节点
  10.  
  11. public BinarySearchTree() {
  12. }
  13.  
  14. // 用一个数组去构建二叉查找树
  15. public TreeNode<Integer> buildBST(Integer[] array) {
  16. if (array.length == 0) {
  17. return null;
  18. } else {
  19. root = null; // 初始化树为空树
  20. for (int i = 0; i < array.length; i++) { // 依次将每个元素插入
  21. root = insertNode(root, array[i]);
  22. }
  23. return root;
  24. }
  25. }
  26.  
  27. // 在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
  28. private TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data) {
  29. if (node == null) { // 原树为空,新插入的记录为根节点
  30. node = new TreeNode<Integer>(data, null, null);
  31. } else {
  32. if (node.data != data) { // 树中不存在相同关键字的结点
  33. if (node.data > data) { // 根节点>插入数据,插入到左子树中
  34. node.lchild = insertNode(node.lchild, data);
  35. } else { // 根节点<插入数据,插入到右子树中
  36. node.rchild = insertNode(node.rchild, data);
  37. }
  38. }
  39. }
  40. return node;
  41. }
  42.  
  43. // 二叉查找树的中序遍历,可以得到一个递增的有序数列
  44. public void inOrder(TreeNode<Integer> node) {
  45. if (node != null) {
  46. inOrder(node.lchild);
  47. System.out.print(node.data + " ");
  48. inOrder(node.rchild);
  49. }
  50. }
  51.  
  52. // 二叉查找树的层次遍历
  53. public void levelOrder(TreeNode<Integer> root) {
  54. Queue<TreeNode<Integer>> nodeQueue = new LinkedList<TreeNode<Integer>>();
  55. TreeNode<Integer> node = null;
  56. nodeQueue.add(root); // 将根节点入队
  57. while (!nodeQueue.isEmpty()) { // 队列不空循环
  58. node = nodeQueue.peek();
  59. System.out.print(node.data + " ");
  60. nodeQueue.poll(); // 队头元素出队
  61. if (node.lchild != null) { // 左子树不空,则左子树入队列
  62. nodeQueue.add(node.lchild);
  63. }
  64. if (node.rchild != null) { // 右子树不空,则右子树入队列
  65. nodeQueue.add(node.rchild);
  66. }
  67. }
  68. }
  69.  
  70. // 查找数据域为data的结点,若不存在,返回null
  71. public TreeNode<Integer> searchNode(TreeNode<Integer> node, Integer data) {
  72. while (node != null && node.data != data) {
  73. if (node.data > data) {
  74. node = node.lchild; // 根节点>数据,向左走
  75. } else {
  76. node = node.rchild; // 根节点<数据,向右走
  77. }
  78. }
  79. return node;
  80. }
  81.  
  82. // 查找最大值:不断地寻找右子节点
  83. public TreeNode<Integer> getMaxData(TreeNode<Integer> node) {
  84. if (node.rchild == null) {
  85. return node;
  86. } else {
  87. return getMaxData(node.rchild);
  88. }
  89. }
  90.  
  91. // 查找最小值:不断地寻找左子节点
  92. public TreeNode<Integer> getMinData(TreeNode<Integer> node) {
  93. if (node.lchild == null) {
  94. return node;
  95. } else {
  96. return getMinData(node.lchild);
  97. }
  98. }
  99.  
  100. // 得到数据域为data的结点的直接父节点parentNode
  101. public TreeNode<Integer> getParentNode(TreeNode<Integer> root, Integer data) {
  102. TreeNode<Integer> parentNode = root;
  103. if (parentNode.data == data) { // 根节点的父节点返回为null
  104. return null;
  105. }
  106. while (parentNode != null) {
  107. // 查找当前节点的父节点的左右子节点,若是相等,则返回该父节点
  108. if ((parentNode.lchild != null && parentNode.lchild.data == data)
  109. || (parentNode.rchild != null && parentNode.rchild.data == data)) {
  110. return parentNode;
  111. } else {
  112. if (parentNode.data > data) { // 向左查找父节点
  113. parentNode = parentNode.lchild;
  114. } else {
  115. parentNode = parentNode.rchild; // 向右查找父节点
  116. }
  117. }
  118. }
  119. return null;
  120. }
  121.  
  122. /**
  123. * 得到结点node的直接前趋 a.该节点左子树不为空:其前驱节点为其左子树的最大元素
  124. * b.该节点左子树为空:其前驱节点为其祖先节点(递归),且该祖先节点的右孩子也为其祖先节点 (就是一直往其parent找,出现左拐后的那个祖先节点)
  125. */
  126. public TreeNode<Integer> getPrecessor(TreeNode<Integer> root, TreeNode<Integer> node) {
  127. if (node == null) {
  128. return null;
  129. }
  130. // a.该节点左子树不为空:其前驱节点为其左子树的最大元素
  131. if (node.lchild != null) {
  132. return getMaxData(node.lchild);
  133. } else { // b.该节点左子树为空: 其前驱节点为其祖先节点(递归)
  134. TreeNode<Integer> parentNode = getParentNode(root, node.data);
  135. while (parentNode != null && node == parentNode.lchild) {
  136. node = parentNode;
  137. parentNode = getParentNode(root, parentNode.data);
  138. }
  139. return parentNode;
  140. }
  141. }
  142.  
  143. /**
  144. * 得到结点node的直接后继(后继节点就是比要删除的节点的关键值要大的节点集合中的最小值) a.该节点右子树不为空,其后继节点为其右子树的最小元素
  145. * b.该节点右子树为空,则其后继节点为其祖先节点(递归),且此祖先节点的左孩子也是该节点的祖先节点,
  146. * 就是说一直往上找其祖先节点,直到出现右拐后的那个祖先节点:
  147. */
  148. public TreeNode<Integer> getSuccessor(TreeNode<Integer> root, TreeNode<Integer> node) {
  149. if (node == null) {
  150. return null;
  151. }
  152. // a.该节点右子树不为空,其后继节点为其右子树的最小元素
  153. if (node.rchild != null) {
  154. return getMinData(node.rchild);
  155. } else { // b.该节点右子树为空,则其后继节点为其最高祖先节点(递归)
  156. TreeNode<Integer> parentNode = getParentNode(root, node.data);
  157. while (parentNode != null && node == parentNode.rchild) {
  158. node = parentNode;
  159. parentNode = getParentNode(root, parentNode.data);
  160. }
  161. return parentNode;
  162. }
  163. }
  164.  
  165. /**
  166. * 删除数据域为data的结点 按三种情况处理: a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质
  167. * b.如果节点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,代替z的位置
  168. * c.若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z,
  169. * 然后从二叉查找树中删去这个直接后继(或直接前驱),这样就转换为第一或第二种情况
  170. *
  171. * @param node
  172. * 二叉查找树的根节点
  173. * @param data
  174. * 需要删除的结点的数据域
  175. * @return
  176. */
  177. public boolean deleteNode(TreeNode<Integer> node, Integer data) {
  178. if (node == null) { // 树为空
  179. throw new RuntimeException("树为空!");
  180. }
  181. TreeNode<Integer> delNode = searchNode(node, data); // 搜索需要删除的结点
  182. TreeNode<Integer> parent = null;
  183. if (delNode == null) { // 如果树中不存在要删除的关键字
  184. throw new RuntimeException("树中不存在要删除的关键字!");
  185. } else {
  186. parent = getParentNode(node, data); // 得到删除节点的直接父节点
  187. // a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质
  188. if (delNode.lchild == null && delNode.rchild == null) {
  189. if (delNode == parent.lchild) { // 被删除节点为其父节点的左孩子
  190. parent.lchild = null;
  191. } else { // 被删除节点为其父节点的右孩子
  192. parent.rchild = null;
  193. }
  194. return true;
  195. }
  196. // b1.如果节点z只有一颗左子树,则让z的子树成为z父节点的子树,代替z的位置
  197. if (delNode.lchild != null && delNode.rchild == null) {
  198. if (delNode == parent.lchild) { // 被删除节点为其父节点的左孩子
  199. parent.lchild = delNode.lchild;
  200. } else { // 被删除节点为其父节点的右孩子
  201. parent.rchild = delNode.lchild;
  202. }
  203. delNode.lchild = null; // 设置被删除结点的左孩子为null
  204. return true;
  205. }
  206. // b2.如果节点z只有一颗右子树,则让z的子树成为z父节点的子树,代替z的位置
  207. if (delNode.lchild == null && delNode.rchild != null) {
  208. if (delNode == parent.lchild) { // 被删除节点为其父节点的左孩子
  209. parent.lchild = delNode.rchild;
  210. } else { // 被删除节点为其父节点的右孩子
  211. parent.rchild = delNode.rchild;
  212. }
  213. delNode.rchild = null; // 设置被删除结点的右孩子为null
  214. return true;
  215. }
  216. // c.若结点z有左、右两颗子树,则删除该结点的后继结点,并用该后继结点取代该结点
  217. if (delNode.lchild != null && delNode.rchild != null) {
  218. TreeNode<Integer> successorNode = getSuccessor(node, delNode); // 得到被删除结点的后继节点
  219. deleteNode(node, successorNode.data); // 删除该结点的后继结点
  220. delNode.data = successorNode.data; // 用该后继结点取代该结点
  221. return true;
  222. }
  223. }
  224. return false;
  225.  
  226. }
  227.  
  228. /**
  229. * 某些方法的非递归实现 1. 插入节点insertNode(): 2. 二叉查找树的中序遍历: 3. 得到二叉查找树的最大值和最小值:
  230. */
  231. // 1. 在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
  232. public TreeNode<Integer> insertNode2(TreeNode<Integer> node, Integer data) {
  233. TreeNode<Integer> newNode = new TreeNode<Integer>(data, null, null);
  234. TreeNode<Integer> tmpNode = node; // 遍历节点
  235. TreeNode<Integer> pnode = null; // 记录当前节点的父节点
  236.  
  237. if (node == null) { // 原树为空,新插入的记录为根节点
  238. node = newNode;
  239. return node;
  240. }
  241. while (tmpNode != null) {
  242. pnode = tmpNode;
  243. if (tmpNode.data == data) { // 树中存在相同关键字的结点,什么也不做
  244. return node;
  245. } else {
  246. if (tmpNode.data > data) { // 根节点>插入数据,插入到左子树中
  247. tmpNode = tmpNode.lchild;
  248. } else { // 根节点<插入数据,插入到右子树中
  249. tmpNode = tmpNode.rchild;
  250. }
  251. }
  252. }
  253. if (pnode.data > data) {
  254. pnode.lchild = newNode;
  255. } else {
  256. pnode.rchild = newNode;
  257. }
  258. return node;
  259. }
  260.  
  261. // 2. 二叉查找树的中序遍历LNR,可以得到一个递增的有序数列
  262. public void inOrder2(TreeNode<Integer> node) {
  263. Stack<TreeNode<Integer>> nodeStack = new Stack<TreeNode<Integer>>();
  264. TreeNode<Integer> tempNode = node; // 遍历指针
  265. while (tempNode != null || !nodeStack.isEmpty()) {
  266. if (tempNode != null) {
  267. nodeStack.push(tempNode);
  268. tempNode = tempNode.lchild;
  269. } else {
  270. tempNode = nodeStack.pop();
  271. System.out.print(tempNode.data + " ");
  272. tempNode = tempNode.rchild;
  273. }
  274. }
  275. }
  276.  
  277. // 3.1 查找最大值:不断地寻找右子节点
  278. public TreeNode<Integer> getMaxData2(TreeNode<Integer> node) {
  279. TreeNode<Integer> tempNode = node;
  280. while (tempNode.rchild != null) {
  281. tempNode = tempNode.rchild;
  282. }
  283. return tempNode;
  284. }
  285.  
  286. // 3.2 查找最小值:不断地寻找左子节点
  287. public TreeNode<Integer> getMinData2(TreeNode<Integer> node) {
  288. TreeNode<Integer> tempNode = node;
  289. while (tempNode.lchild != null) {
  290. tempNode = tempNode.lchild;
  291. }
  292. return tempNode;
  293. }
  294.  
  295. public static void main(String[] args) {
  296. Integer[] array = { 8, 3, 10, 1, 6, 14, 4, 7, 13 };
  297. BinarySearchTree bst = new BinarySearchTree();
  298. TreeNode<Integer> root = bst.buildBST(array);
  299. System.out.print("层次遍历:");
  300. bst.levelOrder(root);
  301.  
  302. System.out.print("\n" + "中序遍历:");
  303. bst.inOrder(root);
  304.  
  305. System.out.println();
  306. System.out.print("得到最大值:");
  307. System.out.println(bst.getMaxData(root).data);
  308. System.out.print("得到最小值:");
  309. System.out.println(bst.getMinData(root).data);
  310.  
  311. System.out.print("向二叉查找树中插入一个节点,请输入需插入节点的数据域:");
  312. Scanner input = new Scanner(System.in);
  313. int data = input.nextInt();
  314. System.out.print("插入节点" + data + "后,中序遍历的结果:");
  315. root = bst.insertNode(root, data);
  316. bst.inOrder(root);
  317.  
  318. System.out.println("\n" + "在二叉查找树中查找元素," + "请输入需要查找的结点值:");
  319. data = input.nextInt();
  320. if (bst.searchNode(root, data) == null) {
  321. System.out.println("false");
  322. } else {
  323. System.out.println("true");
  324. }
  325.  
  326. System.out.println("查找节点的直接父节点," + "请输入需要查找的结点值:");
  327. data = input.nextInt();
  328. System.out.print("节点" + data + "的父节点是:");
  329. if (bst.getParentNode(root, data) == null) {
  330. System.out.println("null");
  331. } else {
  332. System.out.println(bst.getParentNode(root, data).data);
  333. }
  334.  
  335. System.out.println("删除结点," + "请输入需要删除的结点值:");
  336. data = input.nextInt();
  337. if (bst.deleteNode(root, data)) {
  338. System.out.print("删除结点后的层次遍历:");
  339. bst.levelOrder(root);
  340. System.out.print("\n" + "删除结点后的中序遍历:");
  341. bst.inOrder(root);
  342. }
  343.  
  344. }
  345.  
  346. }

逻辑图

数据结构实现(四)二叉查找树java实现的更多相关文章

  1. JVM(四):深入分析Java字节码-下

    JVM(四):深入分析Java字节码-下 在上文中,我们讲解了 Class 文件中的文件标识,常量池等内容.在本文中,我们就详细说一下剩下的指令集内容,阐述其分别代表了什么含义,以及 JVM 团队这样 ...

  2. 四、Android学习第四天——JAVA基础回顾(转)

    (转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 四.Android学习第四天——JAVA基础回顾 这才学习Android的 ...

  3. 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...

  4. Java进阶(四十)Java类、变量、方法修饰符讲解

    Java进阶(四十)Java类.变量.方法修饰符讲解 Java类修饰符 abstract: 将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现. final: 将一个类生命为最终(即非继承类) ...

  5. Java多线程(四)java中的Sleep方法

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  6. [Java并发编程(四)] Java volatile 的理论实践

    [Java并发编程(四)] Java volatile 的理论实践 摘要 Java 语言中的 volatile 变量可以被看作是一种 "程度较轻的 synchronized":与 ...

  7. 二十四、JAVA的NIO和IO的区别

    一.JAVA的NIO和IO 1.NIO:面向缓冲区(buffer)(分为非阻塞模式IO和阻塞模式IO)组成部分:Channels管道,Buffers缓冲区,Selectors选择器 2.IO:面向流( ...

  8. 【图数据结构的遍历】java实现广度优先和深度优先遍历

    [图数据结构的遍历]java实现广度优先和深度优先遍历 宽度优先搜索(BFS)遍历图需要使用队列queue数据结构: 深度优先搜索(DFS, Depth First Search)的实现 需要使用到栈 ...

  9. 2017-2018-2 20165301 实验四《Java面向对象程序设计》实验报告

    2017-2018-2 20165301 实验四<Java面向对象程序设计>实验报告 一.Android Stuidio的安装测试 实验要求: 参考<Java和Android开发学习 ...

  10. 20155201 实验四《Java面向对象程序设计》实验报告

    20155201 实验四<Java面向对象程序设计>实验报告 一.实验内容 1.基于Android Studio开发简单的Android应用并部署测试; 2.了解Android.组件.布局 ...

随机推荐

  1. log4j输出多个自定义日志文件,动态配置路径

    Log4J的配置文件(Configuration File)就是用来设置记录器的级别.存放器和布局的,它可接key=value格式的设置或xml格式的设置信息.通过配置,可以创建出Log4J的运行环境 ...

  2. lim的日常生活

     

  3. CF47A Triangular numbers

    CF47A Triangular numbers 题意翻译 给定一个数n,问你是否存在一个整数i,满足i*(i+1)/2=n. 若存在,输出"YES",否则输出"NO&q ...

  4. 今天我也用上了阿里云的Centos

    Redis官方不支持Windows,第三方实现的64位服务端不稳定,因此在我的忽悠之下,公司出钱买了个阿里云的Centos7,4G内存,30G硬盘.现在我也可以真真正正的玩Centos了,python ...

  5. Python游戏server开发日记(二)绕过GIL启动多线程Python环境

    说道Python和多线程,非常easy想到GIL,GIL意味着仅仅要是用Python做的多线程程序.就无法利用多个CPU. 经过一些失败的尝试后,我也一度觉得GIL是无解的.我们甚至把注意力转向了Ir ...

  6. nyoj 119 士兵杀敌(三) 【线段树】【单点更新】

    题意:. .. 策略如题. 思路:我们先如果仅仅求某一区间的最大值.我们仅仅须要利用线段树的模板.仅仅须要初始化和询问的时候小小的改动一下.改成祖先结点储存的不再是子节点的和而是两个子节点之间的最大值 ...

  7. servletConfig和ServletContext 以及servletContextListener介绍

    <servlet>     <servlet-name>BeerParamTests</servlet-name>     <servlet-class> ...

  8. 【Java并发编程实战】—–synchronized

    在我们的实际应用其中可能常常会遇到这样一个场景:多个线程读或者.写相同的数据,訪问相同的文件等等.对于这样的情况假设我们不加以控制,是非常easy导致错误的. 在java中,为了解决问题,引入临界区概 ...

  9. &lt;LeetCode OJ&gt; 100. Same Tree

    100. Same Tree Total Accepted: 100129 Total Submissions: 236623 Difficulty: Easy Given two binary tr ...

  10. BZOJ 3238 后缀数组+单调栈

    单调栈跑两遍求出来 ht[i]为最小值的那段区间 //By SiriusRen #include <cstdio> #include <cstring> #include &l ...