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

二叉查找树的定义:

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

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

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

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

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

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

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

  3. 插入节点

  4. 查找节点

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

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

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

  8. 删除节点

package dataStructures;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack; public class BinarySearchTree {
private TreeNode<Integer> root = null; // 根节点 public BinarySearchTree() {
} // 用一个数组去构建二叉查找树
public TreeNode<Integer> buildBST(Integer[] array) {
if (array.length == 0) {
return null;
} else {
root = null; // 初始化树为空树
for (int i = 0; i < array.length; i++) { // 依次将每个元素插入
root = insertNode(root, array[i]);
}
return root;
}
} // 在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
private TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data) {
if (node == null) { // 原树为空,新插入的记录为根节点
node = new TreeNode<Integer>(data, null, null);
} else {
if (node.data != data) { // 树中不存在相同关键字的结点
if (node.data > data) { // 根节点>插入数据,插入到左子树中
node.lchild = insertNode(node.lchild, data);
} else { // 根节点<插入数据,插入到右子树中
node.rchild = insertNode(node.rchild, data);
}
}
}
return node;
} // 二叉查找树的中序遍历,可以得到一个递增的有序数列
public void inOrder(TreeNode<Integer> node) {
if (node != null) {
inOrder(node.lchild);
System.out.print(node.data + " ");
inOrder(node.rchild);
}
} // 二叉查找树的层次遍历
public void levelOrder(TreeNode<Integer> root) {
Queue<TreeNode<Integer>> nodeQueue = new LinkedList<TreeNode<Integer>>();
TreeNode<Integer> node = null;
nodeQueue.add(root); // 将根节点入队
while (!nodeQueue.isEmpty()) { // 队列不空循环
node = nodeQueue.peek();
System.out.print(node.data + " ");
nodeQueue.poll(); // 队头元素出队
if (node.lchild != null) { // 左子树不空,则左子树入队列
nodeQueue.add(node.lchild);
}
if (node.rchild != null) { // 右子树不空,则右子树入队列
nodeQueue.add(node.rchild);
}
}
} // 查找数据域为data的结点,若不存在,返回null
public TreeNode<Integer> searchNode(TreeNode<Integer> node, Integer data) {
while (node != null && node.data != data) {
if (node.data > data) {
node = node.lchild; // 根节点>数据,向左走
} else {
node = node.rchild; // 根节点<数据,向右走
}
}
return node;
} // 查找最大值:不断地寻找右子节点
public TreeNode<Integer> getMaxData(TreeNode<Integer> node) {
if (node.rchild == null) {
return node;
} else {
return getMaxData(node.rchild);
}
} // 查找最小值:不断地寻找左子节点
public TreeNode<Integer> getMinData(TreeNode<Integer> node) {
if (node.lchild == null) {
return node;
} else {
return getMinData(node.lchild);
}
} // 得到数据域为data的结点的直接父节点parentNode
public TreeNode<Integer> getParentNode(TreeNode<Integer> root, Integer data) {
TreeNode<Integer> parentNode = root;
if (parentNode.data == data) { // 根节点的父节点返回为null
return null;
}
while (parentNode != null) {
// 查找当前节点的父节点的左右子节点,若是相等,则返回该父节点
if ((parentNode.lchild != null && parentNode.lchild.data == data)
|| (parentNode.rchild != null && parentNode.rchild.data == data)) {
return parentNode;
} else {
if (parentNode.data > data) { // 向左查找父节点
parentNode = parentNode.lchild;
} else {
parentNode = parentNode.rchild; // 向右查找父节点
}
}
}
return null;
} /**
* 得到结点node的直接前趋 a.该节点左子树不为空:其前驱节点为其左子树的最大元素
* b.该节点左子树为空:其前驱节点为其祖先节点(递归),且该祖先节点的右孩子也为其祖先节点 (就是一直往其parent找,出现左拐后的那个祖先节点)
*/
public TreeNode<Integer> getPrecessor(TreeNode<Integer> root, TreeNode<Integer> node) {
if (node == null) {
return null;
}
// a.该节点左子树不为空:其前驱节点为其左子树的最大元素
if (node.lchild != null) {
return getMaxData(node.lchild);
} else { // b.该节点左子树为空: 其前驱节点为其祖先节点(递归)
TreeNode<Integer> parentNode = getParentNode(root, node.data);
while (parentNode != null && node == parentNode.lchild) {
node = parentNode;
parentNode = getParentNode(root, parentNode.data);
}
return parentNode;
}
} /**
* 得到结点node的直接后继(后继节点就是比要删除的节点的关键值要大的节点集合中的最小值) a.该节点右子树不为空,其后继节点为其右子树的最小元素
* b.该节点右子树为空,则其后继节点为其祖先节点(递归),且此祖先节点的左孩子也是该节点的祖先节点,
* 就是说一直往上找其祖先节点,直到出现右拐后的那个祖先节点:
*/
public TreeNode<Integer> getSuccessor(TreeNode<Integer> root, TreeNode<Integer> node) {
if (node == null) {
return null;
}
// a.该节点右子树不为空,其后继节点为其右子树的最小元素
if (node.rchild != null) {
return getMinData(node.rchild);
} else { // b.该节点右子树为空,则其后继节点为其最高祖先节点(递归)
TreeNode<Integer> parentNode = getParentNode(root, node.data);
while (parentNode != null && node == parentNode.rchild) {
node = parentNode;
parentNode = getParentNode(root, parentNode.data);
}
return parentNode;
}
} /**
* 删除数据域为data的结点 按三种情况处理: a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质
* b.如果节点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,代替z的位置
* c.若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z,
* 然后从二叉查找树中删去这个直接后继(或直接前驱),这样就转换为第一或第二种情况
*
* @param node
* 二叉查找树的根节点
* @param data
* 需要删除的结点的数据域
* @return
*/
public boolean deleteNode(TreeNode<Integer> node, Integer data) {
if (node == null) { // 树为空
throw new RuntimeException("树为空!");
}
TreeNode<Integer> delNode = searchNode(node, data); // 搜索需要删除的结点
TreeNode<Integer> parent = null;
if (delNode == null) { // 如果树中不存在要删除的关键字
throw new RuntimeException("树中不存在要删除的关键字!");
} else {
parent = getParentNode(node, data); // 得到删除节点的直接父节点
// a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质
if (delNode.lchild == null && delNode.rchild == null) {
if (delNode == parent.lchild) { // 被删除节点为其父节点的左孩子
parent.lchild = null;
} else { // 被删除节点为其父节点的右孩子
parent.rchild = null;
}
return true;
}
// b1.如果节点z只有一颗左子树,则让z的子树成为z父节点的子树,代替z的位置
if (delNode.lchild != null && delNode.rchild == null) {
if (delNode == parent.lchild) { // 被删除节点为其父节点的左孩子
parent.lchild = delNode.lchild;
} else { // 被删除节点为其父节点的右孩子
parent.rchild = delNode.lchild;
}
delNode.lchild = null; // 设置被删除结点的左孩子为null
return true;
}
// b2.如果节点z只有一颗右子树,则让z的子树成为z父节点的子树,代替z的位置
if (delNode.lchild == null && delNode.rchild != null) {
if (delNode == parent.lchild) { // 被删除节点为其父节点的左孩子
parent.lchild = delNode.rchild;
} else { // 被删除节点为其父节点的右孩子
parent.rchild = delNode.rchild;
}
delNode.rchild = null; // 设置被删除结点的右孩子为null
return true;
}
// c.若结点z有左、右两颗子树,则删除该结点的后继结点,并用该后继结点取代该结点
if (delNode.lchild != null && delNode.rchild != null) {
TreeNode<Integer> successorNode = getSuccessor(node, delNode); // 得到被删除结点的后继节点
deleteNode(node, successorNode.data); // 删除该结点的后继结点
delNode.data = successorNode.data; // 用该后继结点取代该结点
return true;
}
}
return false; } /**
* 某些方法的非递归实现 1. 插入节点insertNode(): 2. 二叉查找树的中序遍历: 3. 得到二叉查找树的最大值和最小值:
*/
// 1. 在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
public TreeNode<Integer> insertNode2(TreeNode<Integer> node, Integer data) {
TreeNode<Integer> newNode = new TreeNode<Integer>(data, null, null);
TreeNode<Integer> tmpNode = node; // 遍历节点
TreeNode<Integer> pnode = null; // 记录当前节点的父节点 if (node == null) { // 原树为空,新插入的记录为根节点
node = newNode;
return node;
}
while (tmpNode != null) {
pnode = tmpNode;
if (tmpNode.data == data) { // 树中存在相同关键字的结点,什么也不做
return node;
} else {
if (tmpNode.data > data) { // 根节点>插入数据,插入到左子树中
tmpNode = tmpNode.lchild;
} else { // 根节点<插入数据,插入到右子树中
tmpNode = tmpNode.rchild;
}
}
}
if (pnode.data > data) {
pnode.lchild = newNode;
} else {
pnode.rchild = newNode;
}
return node;
} // 2. 二叉查找树的中序遍历LNR,可以得到一个递增的有序数列
public void inOrder2(TreeNode<Integer> node) {
Stack<TreeNode<Integer>> nodeStack = new Stack<TreeNode<Integer>>();
TreeNode<Integer> tempNode = node; // 遍历指针
while (tempNode != null || !nodeStack.isEmpty()) {
if (tempNode != null) {
nodeStack.push(tempNode);
tempNode = tempNode.lchild;
} else {
tempNode = nodeStack.pop();
System.out.print(tempNode.data + " ");
tempNode = tempNode.rchild;
}
}
} // 3.1 查找最大值:不断地寻找右子节点
public TreeNode<Integer> getMaxData2(TreeNode<Integer> node) {
TreeNode<Integer> tempNode = node;
while (tempNode.rchild != null) {
tempNode = tempNode.rchild;
}
return tempNode;
} // 3.2 查找最小值:不断地寻找左子节点
public TreeNode<Integer> getMinData2(TreeNode<Integer> node) {
TreeNode<Integer> tempNode = node;
while (tempNode.lchild != null) {
tempNode = tempNode.lchild;
}
return tempNode;
} public static void main(String[] args) {
Integer[] array = { 8, 3, 10, 1, 6, 14, 4, 7, 13 };
BinarySearchTree bst = new BinarySearchTree();
TreeNode<Integer> root = bst.buildBST(array);
System.out.print("层次遍历:");
bst.levelOrder(root); System.out.print("\n" + "中序遍历:");
bst.inOrder(root); System.out.println();
System.out.print("得到最大值:");
System.out.println(bst.getMaxData(root).data);
System.out.print("得到最小值:");
System.out.println(bst.getMinData(root).data); System.out.print("向二叉查找树中插入一个节点,请输入需插入节点的数据域:");
Scanner input = new Scanner(System.in);
int data = input.nextInt();
System.out.print("插入节点" + data + "后,中序遍历的结果:");
root = bst.insertNode(root, data);
bst.inOrder(root); System.out.println("\n" + "在二叉查找树中查找元素," + "请输入需要查找的结点值:");
data = input.nextInt();
if (bst.searchNode(root, data) == null) {
System.out.println("false");
} else {
System.out.println("true");
} System.out.println("查找节点的直接父节点," + "请输入需要查找的结点值:");
data = input.nextInt();
System.out.print("节点" + data + "的父节点是:");
if (bst.getParentNode(root, data) == null) {
System.out.println("null");
} else {
System.out.println(bst.getParentNode(root, data).data);
} System.out.println("删除结点," + "请输入需要删除的结点值:");
data = input.nextInt();
if (bst.deleteNode(root, data)) {
System.out.print("删除结点后的层次遍历:");
bst.levelOrder(root);
System.out.print("\n" + "删除结点后的中序遍历:");
bst.inOrder(root);
} } }

逻辑图

数据结构实现(四)二叉查找树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. NYIST 119 士兵杀敌(三)

    士兵杀敌(三)时间限制:2000 ms | 内存限制:65535 KB难度:5 描述南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比较,计算出 ...

  2. 继续过Hard题目.0207

    接上一篇:http://www.cnblogs.com/charlesblc/p/6364102.html 继续过Hard模式的题目吧.   # Title Editorial Acceptance ...

  3. UVA 12124 UVAlive 3971 Assemble(二分 + 贪心)

    先从中找出性能最好的那个数, 在用钱比較少的去组合,能组出来就表明答案在mid的右边,反之在左边, #include<string.h> #include<map> #incl ...

  4. HDOJ 2682 Tree(最小生成树prim算法)

    Tree Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  5. Android开发之——编码规范

    1. 前言 这份文档参考了 Google Java 编程风格规范和 Google 官方 Android 编码风格规范.该文档仅供参考,只要形成一个统一的风格,见量知其意就可. 2. 源文件基础 2.1 ...

  6. Mysql第四天 数据库设计

    不考虑主备.集群等方案,基于业务上的设计主要是表结构及表间关系的设计. 而关于表中字段主要是依据业务来进行定义,我们能够指定的大概有这么几项: 存储引擎 一般用InnoDB,特殊需求特殊选用 字符集和 ...

  7. 鸟哥Linux私房菜知识点总结3到5章

    感觉自己对Linux的理解一直不够,所以近期翻看了一本<鸟哥的Linux私房菜>.这是一本基础的书,万丈高楼平地起,会的不多但能够学.这是我整理的一些知识点,尽管非常基础.希望和大家共同交 ...

  8. Android App 内存泄露之Thread

    Thread 内存泄露 线程也是造成内存泄露的一个重要的源头.线程产生内存泄露的主要原因在于线程生命周期的不可控. 1.看一下以下是否存在问题 <span style="white-s ...

  9. SOA概念具体解释

    1.概述 1.1基本定义 SOA(Service-Oriented Architecture)既面向服务的体系结构,是一个组件模型.它将应用程序猿的不同功能可是(称为服务)通过定义良好的接口联系起来. ...

  10. caffe—ssd安装教程

    环境: ubuntu16.04 cuda8.0 cudnn5.0 已安装过caffe1.0 tensorflow1.2 编辑过程中出现问题尽量到这里面搜一下:https://github.com/BV ...