二叉查找树的实现

1. 原理

  二叉查找树,又称为二叉排序树、二叉搜索树。对于树中每一个节点X,它的左子树中所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项。二叉查找树的平均深度为O(log N),搜索元素的时间复杂度也是O(log N)。是两种库集合类TreeSet、TreeMap实现的基础。

2. public API

void makeEmpty( )      --> 置空
boolean isEmpty( )     --> 判空
AnyType findMin( )     --> 寻找最小值
AnyType findMax( )     --> 寻找最大值
boolean contains( x ) --> 是否存在元素x
void insert( x )       --> 插入元素x
void remove( x )       --> 删除元素x
void printTree( )     --> 遍历二叉树

3. 核心思想图解:递归

!寻找最小值

此处用递归实现:

!寻找最大值

此处用非递归实现,也可以用递归实现:

!是否存在元素x

从root开始往下找,找到含有项X的节点,则此操作返回true,没有找到则返回false。

!插入元素x

从root开始往下找到合适的插入位置,然后插入。

!删除元素x

从root开始往下找到元素x,找到则删除,并且处理好后续工作。

4. BinarySearchTree代码实现

类中,大量使用方法来调用递归方法的技巧,很好地体现了面向对象的封装性。
 /**
* @author: wenhx
* @date: Created in 2019/10/8 19:41 (之前)
* @description: 二叉查找树的实现
*/
public class BinarySearchTree<AnyType extends Comparable<? super AnyType>> {

/**
* 树的根节点
*/
private BinaryNode<AnyType> root;

/**
* 定义树的节点(内部类)
*/
private static class BinaryNode<AnyType> {

AnyType element; // 元素值
BinaryNode<AnyType> left; // 左孩子
BinaryNode<AnyType> right; // 右孩子

// 节点的构造器:初始化一个树的节点
BinaryNode(AnyType theElement) {
this(theElement, null, null);
}

BinaryNode(AnyType theElement, BinaryNode<AnyType> lt, BinaryNode<AnyType> rt) {
element = theElement;
left = lt;
right = rt;
}
}

/**
* 二叉排序树的构造器:初始化根节点
*/
public BinarySearchTree() {
root = null;
}

/**
* 置空
*/
public void makeEmpty() {
root = null;
}

/**
* 判空
*/
public boolean isEmpty() {
return root == null;
}

/**
* 寻找最小值
*/
public AnyType findMin() {
if (isEmpty()) {
throw new RuntimeException();
}
return findMin(root).element;
}

/**
* 寻找最大值
*/
public AnyType findMax() {
if (isEmpty()) {
throw new RuntimeException();
}
return findMax(root).element;
}


/**
* 是否存在元素x
*/
public boolean contains(AnyType x) {
return contains(x, root);
}

/**
* 插入元素x
*/
public void insert(AnyType x) {
root = insert(x, root);
}

/**
* 删除元素x
*/
public void remove(AnyType x) {
root = remove(x, root);
}

/**
* 遍历此二叉树
*/
public void printTree() {
if (isEmpty()) {
System.out.println("Empty tree");
} else {
printTree(root);
}
}

/**
* 寻找最小值(内部方法):此处用递归实现
*/
private BinaryNode<AnyType> findMin(BinaryNode<AnyType> t) {
if (t == null) {
return null;
} else if (t.left == null) {
return t;
}
return findMin(t.left);
}

/**
* 寻找最大值(内部方法):此处用非递归实现
*/
private BinaryNode<AnyType> findMax(BinaryNode<AnyType> t) {
if (t != null) {
while (t.right != null) {
t = t.right;
}
}
return t;
}

/**
* 是否存在元素x(内部方法)
*/
private boolean contains(AnyType x, BinaryNode<AnyType> t) {
/**
* 跳出递归的条件
*/
if (t == null) {
return false;
}

/**
* 如果x小于节点值,则递归到左孩子;
* 如果x大于节点值,则递归到右孩子;
* 如果x等于节点值,则找到。
*/
int compareResult = x.compareTo(t.element);

if (compareResult < 0) {
return contains(x, t.left);
} else if (compareResult > 0) {
return contains(x, t.right);
} else {
return true;
}

}

/**
* 插入元素x(内部方法)
*/
private BinaryNode<AnyType> insert(AnyType x, BinaryNode<AnyType> t) {
/**
* 跳出递归的条件
*/
if (t == null) {
return new BinaryNode<>(x, null, null);
}

/**
* 如果x小于节点值,则递归到左孩子;
* 如果x大于节点值,则递归到右孩子;
* 如果x等于节点值,则说明已有元素x,无需操作。
*/
int compareResult = x.compareTo(t.element);

if (compareResult < 0) {
t.left = insert(x, t.left);
} else if (compareResult > 0) {
t.right = insert(x, t.right);
} else {
}
return t;

}

/**
* 删除元素x(内部方法)
*/
private BinaryNode<AnyType> remove(AnyType x, BinaryNode<AnyType> t) {
/**
* 跳出递归的条件
*/
if (t == null) {
return t; // Item not found; do nothing
}

/**
* 如果x小于节点值,则递归到左孩子;
* 如果x大于节点值,则递归到右孩子;
* 如果x等于节点值,则要删除此节点。
*/
int compareResult = x.compareTo(t.element);

if (compareResult < 0) {
t.left = remove(x, t.left);
} else if (compareResult > 0) {
t.right = remove(x, t.right);
} else if (t.left != null && t.right != null) {
// 要删除的节点有两个孩子(可选用右孩子最小元素/左孩子最大元素上调)
t.element = findMin(t.right).element;
t.right = remove(t.element, t.right);
} else {
// 要删除的节点有一个孩子或者没有孩子
t = (t.left != null) ? t.left : t.right;
}
return t;
}

/**
* 遍历此二叉树(内部方法)
*/
private void printTree(BinaryNode<AnyType> t) {
// 中序遍历-->即递增顺序
if (t != null) {
printTree(t.left);
System.out.println(t.element);
printTree(t.right);
}
}

/**
* 求树的深度(内部方法)
*/
private int height(BinaryNode<AnyType> t) {
if (t == null) {
return -1;
} else {
return 1 + Math.max(height(t.left), height(t.right));
}
}

/**
* 主方法用来测试
*/
public static void main(String[] args) {
BinarySearchTree<Integer> t = new BinarySearchTree<>();
t.insert(6);
t.insert(3);
t.insert(9);
t.insert(2);
t.insert(5);
t.insert(8);
t.insert(10);
t.printTree();
t.insert(4);
}
}

okay,今天就到这啦,一定要掌握这种数据结构哈,真的很重要!!!

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

  1. 数据结构:JAVA实现二叉查找树

    数据结构:JAVA实现二叉查找树 写在前面 二叉查找树(搜索树)是一种能将链表插入的灵活性与有序数组查找的高效性结合在一起的一种数据结构. 观察二叉查找树,我们发现任何一个节点大于左子节点且小于其右子 ...

  2. Java实现二叉查找树

    摘要:一个二叉查找树的Java实现.可以学习二叉树处理的递归及非递归技巧. 难度:初级. 为了克服对树结构编程的恐惧感,决心自己实现一遍二叉查找树,以便掌握关于树结构编程的一些技巧和方法.以下是基本思 ...

  3. JAVA数据结构--二叉查找树

    二叉查找树定义 二叉查找树(英语:Binary Search Tree),也称二叉搜索树.有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tr ...

  4. 使用java实现二叉查找树的插入,修改和删除方法

    目前使用的是根据key的hashcode来进行排序,并且没有考虑hash碰撞的问题 package com.zhou.tree; import java.util.Comparator; import ...

  5. 二叉查找树(三)之 Java的实现

    概要 在前面分别介绍了"二叉查找树的相关理论知识,然后给出了二叉查找树的C和C++实现版本".这一章写一写二叉查找树的Java实现版本. 目录 1. 二叉树查找树2. 二叉查找树的 ...

  6. 红黑树(五)之 Java的实现

    概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...

  7. Java数据结构和算法(四)赫夫曼树

    Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...

  8. Java数据结构和算法(二)树的基本操作

    Java数据结构和算法(二)树的基本操作 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 一.树的遍历 二叉树遍历分为:前序遍 ...

  9. 红黑树 Java实现

    概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...

随机推荐

  1. [译]Vulkan教程(12)图形管道基础之入门

    [译]Vulkan教程(12)图形管道基础之入门 Introduction 入门 Over the course of the next few chapters we'll be setting u ...

  2. Metasploit从文件中读取目标地址

    本文简单介绍如何使用Metasploit从文件中读取目标地址,来执行检测. 以检测MS17-010漏洞为例,在设定RHOSTS参数时,可设定目标地址范围和CIDR地址块,设定单个IP的目标也是可以的. ...

  3. java 单链表 练习

    练习一下java单链表的简单习题 package com.test1; import java.util.Stack; public class SingleListDemo { /** * 返回单链 ...

  4. ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

    现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间.我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作 ...

  5. 爬虫爬取m3u8视频文件

    一.m3u8视频格式 一般m3u8文件和 视频流ts文件放在同一目录 而m3u8文件格式存放的一般都是ts 文件的一个列表 二.根据m3u8视频存放以及写法的规律 思路 我们一般网站上能找到的m3u8 ...

  6. GO-&获取地址与*解引用

    &变量 获取变量在内存空间的地址 *变量地址 获取变量的值 一.普通数据 package main import "fmt" func main(){ b :=1111 c ...

  7. GO基础之函数的高级用法

    一.可变参数 支持可变长参数列表的函数可以支持任意个传入参数,比如fmt.Println函数就是一个支持可变长参数列表的函数. package main import "fmt" ...

  8. 看完这篇文章,我奶奶都知道什么是JVM中的内存模型与垃圾回收!

    扩展阅读:JVM从入门开始深入每一个底层细节 六.内存模型 6.1.内存模型与运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同数据区域. Java内存模型的主要目 ...

  9. JS基础语法---Date对象中常见的方法

    创建实例对象 var dt = new Date(); //当前的时间---当前的服务器 console.log(dt); var dt = new Date("2017-08-12&quo ...

  10. python的exe反编译

    目录 python的exe反编译 方法一.使用archive_viewer.py提取pyc 方法二.使用pyinstxtractor.py提取pyc python的exe反编译 驱动人生样本为pyth ...