二叉查找树的定义:

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

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

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

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

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

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

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

  3. 插入节点

  4. 查找节点

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

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

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

  8. 删除节点

树节点TreeNode的定义见:Java实现链式存储的二叉树

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

程序运行结果:

  1. 层次遍历:8 3 10 1 6 14 4 7 13
  2. 中序遍历:1 3 4 6 7 8 10 13 14
  3. 得到最大值:14
  4. 得到最小值:1
  5. 向二叉查找树中插入一个节点,请输入需插入节点的数据域:15
  6. 插入节点15后,中序遍历的结果:1 3 4 6 7 8 10 13 14 15
  7. 在二叉查找树中查找元素,请输入需要查找的结点值:
  8. 4
  9. true
  10. 查找节点的直接父节点,请输入需要查找的结点值:
  11. 10
  12. 节点10的父节点是:8
  13. 删除结点,请输入需要删除的结点值:
  14. 4
  15. 删除结点后的层次遍历:8 3 10 1 6 14 7 13 15
  16. 删除结点后的中序遍历:1 3 6 7 8 10 13 14 15

某些方法的非递归实现:

1. 插入节点insertNode():

  1.   //在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
  2. public TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data){
  3. TreeNode<Integer> newNode = new TreeNode<Integer>(data,null,null);
  4. TreeNode<Integer> tmpNode = node; //遍历节点
  5. TreeNode<Integer> pnode = null; //记录当前节点的父节点
  6.  
  7. if(node == null){ //原树为空,新插入的记录为根节点
  8. node = newNode;
  9. return node;
  10. }
  11. while(tmpNode != null){
  12. pnode = tmpNode;
  13. if(tmpNode.getData() == data){ //树中存在相同关键字的结点,什么也不做
  14. return node;
  15. }else{
  16. if(tmpNode.getData() > data){ //根节点>插入数据,插入到左子树中
  17. tmpNode = tmpNode.getLchild();
  18. }else{ //根节点<插入数据,插入到右子树中
  19. tmpNode = tmpNode.getRchild();
  20. }
  21. }
  22. }
  23. if(pnode.getData() > data){
  24. pnode.setLchild(newNode);
  25. }else{
  26. pnode.setRchild(newNode);
  27. }
  28. return node;
  29. }

2. 二叉查找树的中序遍历:

  1.   //二叉查找树的中序遍历LNR,可以得到一个递增的有序数列
  2. public void inOrder(TreeNode<Integer> node){
  3. Stack<TreeNode<Integer>> nodeStack = new Stack<TreeNode<Integer>>();
  4. TreeNode<Integer> tempNode = node; //遍历指针
  5. while(tempNode != null || !nodeStack.isEmpty()){
  6. if(tempNode != null){
  7. nodeStack.push(tempNode);
  8. tempNode = tempNode.getLchild();
  9. }else{
  10. tempNode = nodeStack.pop();
  11. System.out.print(tempNode.getData() + " ");
  12. tempNode = tempNode.getRchild();
  13. }
  14. }
  15. }

3. 得到二叉查找树的最大值和最小值:

  1. //查找最大值:不断地寻找右子节点
  2. public TreeNode<Integer> getMaxData(TreeNode<Integer> node){
  3. TreeNode<Integer> tempNode = node;
  4. while(tempNode.getRchild()!=null){
  5. tempNode = tempNode.getRchild();
  6. }
  7. return tempNode;
  8. }
  9.  
  10. //查找最小值:不断地寻找左子节点
  11. public TreeNode<Integer> getMinData(TreeNode<Integer> node){
  12. TreeNode<Integer> tempNode = node;
  13. while(tempNode.getLchild() != null){
  14. tempNode = tempNode.getLchild();
  15. }
  16. return tempNode;
  17. }

Java实现链式存储的二叉查找树(递归方法)的更多相关文章

  1. Java实现链式存储的二叉树

    二叉树的定义: 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要 ...

  2. 线性表的Java实现--链式存储(单向链表)

    单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素.由于不需要按顺序存储,链表在 ...

  3. 线性表的Java实现--链式存储(双向链表)

    有了单向链表的基础,双向链表的实现就容易多了. 双向链表的一般情况: 增加节点: 删除节点: 双向链表的Java实现: package com.liuhao.algorithm;      publi ...

  4. 【Java】 大话数据结构(6) 栈的顺序与链式存储

    本文根据<大话数据结构>一书,实现了Java版的栈的顺序存储结构.两栈共享空间.栈的链式存储机构. 栈:限定仅在表尾进行插入和删除操作的线性表. 栈的插入(进栈)和删除(出栈)操作如下图所 ...

  5. java资料——顺序存储结构和链式存储结构(转)

    顺序存储结构 主要优点 节省存储空间,随机存取表中元素 缺    点 插入和删除操作需要移动元素 在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构. 顺序存储结 ...

  6. javascript实现数据结构:线性表--线性链表(链式存储结构)

    上一节中, 线性表的顺序存储结构的特点是逻辑关系上相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,它的存储位置可用一个简单,直观的公式来表示.然后,另一方面来看,这个特点也造成这种存储 ...

  7. C++线性表的链式存储结构

    C++实现线性表的链式存储结构: 为了解决顺序存储不足:用线性表另外一种结构-链式存储.在顺序存储结构(数组描述)中,元素的地址是由数学公式决定的,而在链式储存结构中,元素的地址是随机分布的,每个元素 ...

  8. C语言实现链表(链式存储结构)

    链表(链式存储结构)及创建 链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据.与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其 ...

  9. 栈的链式存储 - API实现

    基本概念 其它概念详情參看前一篇博文:栈的顺序存储 - 设计与实现 - API实现 这里也是运用了链表的链式存储API高速实现了栈的API. 代码: // linkstack.h // 链式存储栈的A ...

随机推荐

  1. 团队作业之404 Note Found Team

    如果记忆是一个罐头的话,我希望这一罐罐头不会过期----<重庆森林> 404 Note Found Team 如果记忆是一个备忘录的话,别说了,它不会过期----<404 Note ...

  2. 微信小程序之Flex布局

    微信小程序页面布局方式采用的是Flex布局.Flex布局,是W3c在2009年提出的一种新的方案,可以简便,完整,响应式的实现各种页面布局.Flex布局提供了元素在容器中的对齐,方向以及顺序,甚至他们 ...

  3. CSS+JS笔记

    CSS篇: 1.a标签去掉下划线 a { text-decoration:none; }

  4. C/C++ 打印文件名、行号、函数名的方法

    转自:http://zhidao.baidu.com/link?url=JLCaxBAXLJVcx_8jsyJVF92E_bZjo4ONJ5Ab-HGlNBc1dfzcAyFAIygwP1qr18aa ...

  5. 软工网络15团队作业8——Beta阶段敏捷冲刺(Day5)

    提供当天站立式会议照片一张 每个人的工作 1.讨论项目每个成员的昨天进展 赵铭: 进一步数据整理,写入数据库. 吴慧婷:主页面.查单词页面的改进.背单词界面改进. 陈敏: 单词学习功能及该界面按钮功能 ...

  6. Excel作为数据源TesTNG做数据驱动完整代码

    说明:EXCEL 支持xls 和xlsx 俩种格式 : 已经过测试 ! package main.java; import org.apache.poi.ss.usermodel.*; import ...

  7. git使用教程推荐

    Git使用教程 一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是 ...

  8. SCRIPT7002: XMLHttpRequest: 网络错误 0x2efe, 由于出现错误 00002efe 而导致此项操作无法完成

    google中带中文参数可能查询,但是在IE带中文参数不能查询:报如下错误 SCRIPT7002: XMLHttpRequest: 网络错误 0x2efe, 由于出现错误 00002efe 而导致此项 ...

  9. 虚拟机centos 安装 redis 环境 linux 使用 java 远程连接 redis

    redis官网地址:http://www.redis.io/ 最新版本:2.8.3 在Linux下安装Redis非常简单,具体步骤如下(官网有说明): 1.下载源码,解压缩后编译源码. $ wget ...

  10. javascript中对象访问自身属性的方式

    在javascript中,通过对象的方法访问对象自身属性时,必须采用this.fieldName的方式. 原因是javascript中Function是无状态的,访问对象的属性时,必须指定当前的上下文 ...