二叉树(binary)是一种特殊的树。二叉树的每个节点最多只能有2个子节点:

二叉树

由于二叉树的子节点数目确定,所以可以直接采用上图方式在内存中实现。每个节点有一个左子节点(left children)和右子节点(right children)。左子节点是左子树的根节点,右子节点是右子树的根节点。

如果我们给二叉树加一个额外的条件,就可以得到一种被称作二叉搜索树(binary search tree)的特殊二叉树。二叉搜索树要求:每个节点都不比它左子树的任意元素小,而且不比它的右子树的任意元素大。

(如果我们假设树中没有重复的元素,那么上述要求可以写成:每个节点比它左子树的任意节点大,而且比它右子树的任意节点小)

二叉搜索树,注意树中元素的大小

二叉搜索树可以方便的实现搜索算法。在搜索元素x的时候,我们可以将x和根节点比较:

1. 如果x等于根节点,那么找到x,停止搜索 (终止条件)

2. 如果x小于根节点,那么搜索左子树

3. 如果x大于根节点,那么搜索右子树

二叉搜索树所需要进行的操作次数最多与树的深度相等。n个节点的二叉搜索树的深度最多为n,最少为log(n)。

下面是用java实现的二叉搜索树,并有搜索,插入,删除,寻找最大最小节点的操作。

删除节点相对比较复杂。删除节点后,有时需要进行一定的调整,以恢复二叉搜索树的性质(每个节点都不比它左子树的任意元素小,而且不比它的右子树的任意元素大)。

  • 叶节点可以直接删除。
  • 删除非叶节点时,比如下图中的节点8,我们可以删除左子树中最大的元素(或者右树中最大的元素),用删除的节点来补充元素8产生的空缺。但该元素可能也不是叶节点,所以它所产生的空缺需要其他元素补充…… 直到最后删除一个叶节点。上述过程可以递归实现。

删除节点

删除节点后的二叉搜索树

import java.util.ArrayList;
import java.util.List; public class BinarySearchTree { // 树的根结点
private TreeNode root = null; // 遍历结点列表
private List<TreeNode> nodelist = new ArrayList<TreeNode>(); private class TreeNode { private int key;
private TreeNode leftChild;
private TreeNode rightChild;
private TreeNode parent; public TreeNode(int key, TreeNode leftChild, TreeNode rightChild,
TreeNode parent) {
this.key = key;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.parent = parent;
} public int getKey() {
return key;
} public String toString() {
String leftkey = (leftChild == null ? "" : String
.valueOf(leftChild.key));
String rightkey = (rightChild == null ? "" : String
.valueOf(rightChild.key));
return "(" + leftkey + " , " + key + " , " + rightkey + ")";
} } /**
* isEmpty: 判断二叉查找树是否为空;若为空,返回 true ,否则返回 false .
*
*/
public boolean isEmpty() {
if (root == null) {
return true;
} else {
return false;
}
} /**
* TreeEmpty: 对于某些二叉查找树操作(比如删除关键字)来说,若树为空,则抛出异常。
*/
public void TreeEmpty() throws Exception {
if (isEmpty()) {
throw new Exception("树为空!");
}
} /**
* search: 在二叉查找树中查询给定关键字
*
* @param key
* 给定关键字
* @return 匹配给定关键字的树结点
*/
public TreeNode search(int key) {
TreeNode pNode = root;
while (pNode != null && pNode.key != key) {
if (key < pNode.key) {
pNode = pNode.leftChild;
} else {
pNode = pNode.rightChild;
}
}
return pNode;
} /**
* minElemNode: 获取二叉查找树中的最小关键字结点
*
* @return 二叉查找树的最小关键字结点
* @throws Exception
* 若树为空,则抛出异常
*/
public TreeNode minElemNode(TreeNode node) throws Exception {
if (node == null) {
throw new Exception("树为空!");
}
TreeNode pNode = node;
while (pNode.leftChild != null) {
pNode = pNode.leftChild;
}
return pNode;
} /**
* maxElemNode: 获取二叉查找树中的最大关键字结点
*
* @return 二叉查找树的最大关键字结点
* @throws Exception
* 若树为空,则抛出异常
*/
public TreeNode maxElemNode(TreeNode node) throws Exception {
if (node == null) {
throw new Exception("树为空!");
}
TreeNode pNode = node;
while (pNode.rightChild != null) {
pNode = pNode.rightChild;
}
return pNode;
} /**
* successor: 获取给定结点在中序遍历顺序下的后继结点
*
* @param node
* 给定树中的结点
* @return 若该结点存在中序遍历顺序下的后继结点,则返回其后继结点;否则返回 null
* @throws Exception
*/
public TreeNode successor(TreeNode node) throws Exception {
if (node == null) {
return null;
} // 若该结点的右子树不为空,则其后继结点就是右子树中的最小关键字结点
if (node.rightChild != null) {
return minElemNode(node.rightChild);
}
// 若该结点右子树为空
TreeNode parentNode = node.parent;
while (parentNode != null && node == parentNode.rightChild) {
node = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
} /**
* precessor: 获取给定结点在中序遍历顺序下的前趋结点
*
* @param node
* 给定树中的结点
* @return 若该结点存在中序遍历顺序下的前趋结点,则返回其前趋结点;否则返回 null
* @throws Exception
*/
public TreeNode precessor(TreeNode node) throws Exception {
if (node == null) {
return null;
} // 若该结点的左子树不为空,则其前趋结点就是左子树中的最大关键字结点
if (node.leftChild != null) {
return maxElemNode(node.leftChild);
}
// 若该结点左子树为空
TreeNode parentNode = node.parent;
while (parentNode != null && node == parentNode.leftChild) {
node = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
} /**
* insert: 将给定关键字插入到二叉查找树中
*
* @param key
* 给定关键字
*/
public void insert(int key) {
TreeNode parentNode = null;
TreeNode newNode = new TreeNode(key, null, null, null);
TreeNode pNode = root;
if (root == null) {
root = newNode;
return;
}
while (pNode != null) {
parentNode = pNode;
if (key < pNode.key) {
pNode = pNode.leftChild;
} else if (key > pNode.key) {
pNode = pNode.rightChild;
} else {
// 树中已存在匹配给定关键字的结点,则什么都不做直接返回
return;
}
}
if (key < parentNode.key) {
parentNode.leftChild = newNode;
newNode.parent = parentNode;
} else {
parentNode.rightChild = newNode;
newNode.parent = parentNode;
} } /**
* insert: 从二叉查找树中删除匹配给定关键字相应的树结点
*
* @param key
* 给定关键字
*/
public void delete(int key) throws Exception {
TreeNode pNode = search(key);
if (pNode == null) {
throw new Exception("树中不存在要删除的关键字!");
}
delete(pNode);
} /**
* delete: 从二叉查找树中删除给定的结点.
*
* @param pNode
* 要删除的结点
*
* 前置条件: 给定结点在二叉查找树中已经存在
* @throws Exception
*/
private void delete(TreeNode pNode) throws Exception {
if (pNode == null) {
return;
}
if (pNode.leftChild == null && pNode.rightChild == null) { // 该结点既无左孩子结点,也无右孩子结点
TreeNode parentNode = pNode.parent;
if (pNode == parentNode.leftChild) {
parentNode.leftChild = null;
} else {
parentNode.rightChild = null;
}
return;
}
if (pNode.leftChild == null && pNode.rightChild != null) { // 该结点左孩子结点为空,右孩子结点非空
TreeNode parentNode = pNode.parent;
if (pNode == parentNode.leftChild) {
parentNode.leftChild = pNode.rightChild;
pNode.rightChild.parent = parentNode;
} else {
parentNode.rightChild = pNode.rightChild;
pNode.rightChild.parent = parentNode;
}
return;
}
if (pNode.leftChild != null && pNode.rightChild == null) { // 该结点左孩子结点非空,右孩子结点为空
TreeNode parentNode = pNode.parent;
if (pNode == parentNode.leftChild) {
parentNode.leftChild = pNode.leftChild;
pNode.rightChild.parent = parentNode;
} else {
parentNode.rightChild = pNode.leftChild;
pNode.rightChild.parent = parentNode;
}
return;
}
// 该结点左右孩子结点均非空,则删除该结点的后继结点,并用该后继结点取代该结点
TreeNode successorNode = successor(pNode);
delete(successorNode);
pNode.key = successorNode.key;
} /**
* inOrderTraverseList: 获得二叉查找树的中序遍历结点列表
*
* @return 二叉查找树的中序遍历结点列表
*/
public List<TreeNode> inOrderTraverseList() {
if (nodelist != null) {
nodelist.clear();
}
inOrderTraverse(root);
return nodelist;
} /**
* inOrderTraverse: 对给定二叉查找树进行中序遍历
*
* @param root
* 给定二叉查找树的根结点
*/
private void inOrderTraverse(TreeNode root) {
if (root != null) {
inOrderTraverse(root.leftChild);
nodelist.add(root);
inOrderTraverse(root.rightChild);
}
} /**
* toStringOfOrderList: 获取二叉查找树中关键字的有序列表
*
* @return 二叉查找树中关键字的有序列表
*/
public String toStringOfOrderList() {
StringBuilder sbBuilder = new StringBuilder(" [ ");
for (TreeNode p : inOrderTraverseList()) {
sbBuilder.append(p.key);
sbBuilder.append(" ");
}
sbBuilder.append("]");
return sbBuilder.toString();
} /**
* 获取该二叉查找树的字符串表示
*/
public String toString() {
StringBuilder sbBuilder = new StringBuilder(" [ ");
for (TreeNode p : inOrderTraverseList()) {
sbBuilder.append(p);
sbBuilder.append(" ");
}
sbBuilder.append("]");
return sbBuilder.toString();
} public TreeNode getRoot() {
return root;
} public static void testNode(BinarySearchTree bst, TreeNode pNode)
throws Exception {
System.out.println("本结点: " + pNode);
System.out.println("前趋结点: " + bst.precessor(pNode));
System.out.println("后继结点: " + bst.successor(pNode));
} public static void testTraverse(BinarySearchTree bst) {
System.out.println("二叉树遍历:" + bst);
System.out.println("二叉查找树转换为有序列表: " + bst.toStringOfOrderList());
} public static void main(String[] args) {
try {
BinarySearchTree bst = new BinarySearchTree();
System.out.println("查找树是否为空? " + (bst.isEmpty() ? "是" : "否"));
int[] keys = new int[] { 15, 6, 18, 3, 7, 13, 20, 2, 9, 4 };
for (int key : keys) {
bst.insert(key);
}
System.out.println("查找树是否为空? " + (bst.isEmpty() ? "是" : "否"));
TreeNode minkeyNode = bst.minElemNode(bst.getRoot());
System.out.println("最小关键字: " + minkeyNode.getKey());
testNode(bst, minkeyNode);
TreeNode maxKeyNode = bst.maxElemNode(bst.getRoot());
System.out.println("最大关键字: " + maxKeyNode.getKey());
testNode(bst, maxKeyNode);
System.out.println("根结点关键字: " + bst.getRoot().getKey());
testNode(bst, bst.getRoot());
testTraverse(bst);
System.out.println("****************************** ");
testTraverse(bst);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
} }
  

  

java实现二叉树查找树的更多相关文章

  1. 手把手教你用java实现二分查找树及其相关操作

    二分查找树(Binary Search Tree)的基本操作有搜索.求最大值.求最小值.求前继.求后继.插入及删除. 对二分查找树的进行基本操作所花费的时间与树的高度成比例.例如有n个节点的完全二叉树 ...

  2. 2-3 查找树及其Java实现

    2-3 查找树 定义(来源:wiki) 查找 插入 2-3 查找树 定义(来源:wiki) 2–3树是一种树型数据结构,内部节点(存在子节点的节点)要么有2个孩子和1个数据元素,要么有3个孩子和2个数 ...

  3. 树的基本概念以及java实现二叉树

    树具有的特点有: (1)每个结点有零个或多个子结点 (2)没有父节点的结点称为根节点 (3)每一个非根结点有且只有一个父节点 (4)除了根结点外,每个子结点可以分为多个不相交的子树.   树的基本术语 ...

  4. Java数据结构(十五)—— 多路查找树

    多路查找树 二叉树和B树 二叉树的问题分析 二叉树操作效率高 二叉树需要加载到内存,若二叉树的节点多存在如下问题: 问题1:构建二叉树时,需多次进行I/O操作,对与速度有影响 问题2:节点海量造成二叉 ...

  5. 数据结构:JAVA_二叉数查找树基本实现(中)

    数据结构:二叉数查找树基本实现(JAVA语言版) 1.写在前面 二叉查找树得以广泛应用的一个重要原因是它能保持键的有序性,因此我们可以把它作为实现有序符号表API中的众多方法的基础. 也就是说我们构建 ...

  6. 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

    http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...

  7. (2)Java数据结构--二叉树 -和排序算法实现

    === 注释:此人博客对很多个数据结构类都有讲解-并加以实例 Java API —— ArrayList类 & Vector类 & LinkList类Java API —— BigDe ...

  8. 自平衡二叉(查找树/搜索树/排序树) binary search tree

    在计算机科学中,AVL树是最先发明的自平衡二叉查找树.AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 "An alg ...

  9. 从Java看数据结构之——树和他的操作集

    写在前面 树这种数据结构在计算机世界中有广泛的应用,比如操作系统中用到了红黑树,数据库用到了B+树,编译器中的语法树,内存管理用到了堆(本质上也是树),信息论中的哈夫曼编码等等等等.而树的实现和他的操 ...

随机推荐

  1. hive学习3(hive基本操作)

    hive基本操作 hive的数据类型 1)基本数据类型 TINYINT,SMALLINT,INT,BIGINT FLOAT/DOUBLE BOOLEAN STRING 2)复合类型 ARRAY:一组有 ...

  2. 基本语法 protocols Category extension

    转:http://blog.csdn.net/wangeen/article/details/16989529 protocol   本质就是一系列的method的声明,他并不像class是必须的选项 ...

  3. Window Server 2008 R2 TFS2010的安装和配置

    1.打开Setup进行安装 2.下一步,然后功能全选 3.点击安装,便开始安装了 安装成功 配置 进行配置之后,选择高级,因为其他功能可能没那么多 到如下界面后,直接进行下一步就可以 下一步,设置TF ...

  4. netstat(转载)

    简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Member ...

  5. YChaos生成混沌图像

    YChaos是一款通过数学公式生成混沌图像的软件,展示混沌之美,数学之美.软件中定义一套简易的脚本语言,用于描述数学表达式.使用时需要先要将数学表达式写成该脚本的形式,解析脚本代码以生成相应的图形与图 ...

  6. 将redis作为windows系统的系统服务

    在windows系统,每次打开redis服务都是一个dos窗口一直开着,很不爽,这里我用winsw工具把redis作为系统服务启动 我把自动启动的所有内容都整理好了,请自行下载 下载地址:http:/ ...

  7. CoreData多线程安全

    CoreData中的NSManagedObjectContext在多线程中不安全,如果想要多线程访问CoreData的话,最好的方法是一个线程一个NSManagedObjectContext, ,每个 ...

  8. Codeforces Round #381 (Div. 1) B. Alyona and a tree dfs序 二分 前缀和

    B. Alyona and a tree 题目连接: http://codeforces.com/contest/739/problem/B Description Alyona has a tree ...

  9. 常用的sql脚本 游标遍历操作

    Declare @id int DECLARE cursor_data CURSOR FOR --这里是取得数据源 OPEN cursor_data FETCH FROM cursor_data IN ...

  10. git常用命令-基本操作

    git常用命令-基本操作 1)      新增文件 新增了Test_1.java git add Test_1.java git commit –m “新增了Test_1.java” git push ...