二叉查找树 Java实现
定义:
一棵二叉查找树是一棵二叉树,每个节点都含有一个Comparable的键(以及对应的值)。
每个节点的键都大于左子树中任意节点的键而小于右子树中任意节点的键。
树的术语:
| Name | Function |
|---|---|
| 路径 | 顺着连接点的边从一个节点走向另一个节点,所经过的节点的顺序排列就称为路径。 |
| 根 | 树顶端的节点就称为根,一棵树只有一个根,如果要把一个节点和边的集合定义为树,那么从根到其他任何一个节点都必须有一条路径。 |
| 父节点 | 每个节点(除了根)都恰好有一条边向上连接到另一个节点,上面的节点就称为下面节点的“父节点”。 |
| 子节点 | 每个节点都可能有一条或多条边向下连接其他节点,下面的这些节点就称为它的“子节点”。 |
| 叶节点 | 没有子节点的节点称为“叶子节点”或简称“叶节点”。树只能有一个根,但是可以有很多叶节点。 |
| 子树 | 每个节点都可以作为子树的根,它和它所有的子节点,子节点的子节点等都含在子树中。 |
| 访问 | 当程序控制流程到达某个节点的时候,就称为“访问”这个节点,通常是为了在这个节点处执行某种操作,例如查看节点某个数据字段的值或者显示节点。 |
| 遍历 | 遍历树意味着要遵循某种特定的顺序访问树中的所有节点。 |
| 层 | 一个节点的层数是指从根开始到这个节点有多少“代”。 |
| 关键字 | 可以看到,对象中通常会有一个数据域被指定为关键字值。这个值通常用于查询或者其他操作。 |
| 二叉树 | 如果树中的每个节点最多只能有两个子节点,这样的树就称为“二叉树”。 |
性质:
- 若它的左子树不为空,则左子树上的所有节点的值都小于它的根节点的值;
- 若它的右子树不为空,则右子树上所有节点的值都大于它的根节点的值;
- 其他的左右子树也分别为二叉查找树;
- 二叉查找树是动态查找表,在查找的过程中可见添加和删除相应的元素,在这些操作中需要保持二叉查找树的以上性质。
根据其二叉树的特性,节点类如下:
public class Node {
public int index;//关键字段
public String data;//值
public Node leftNode;//左节点
public Node rightNode;//右节点
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
其中引用了commons-lang3包中的内容,作为对象进行比较
查找某个节点,相当于二分查找,如果小于当前节点,则走左边,如果大于当前节点,则走右边。当最后叶子节点还没有找到,则没有找到
public Node findNode(int key){
Node current = root;
while(current.index != key){
if(key < current.index){//左节点
current = current.leftNode;
}else{//右节点
current = current.rightNode;
}
if(current == null){
return null;
}
}
return current;
}
插入节点,按照插入的节点都不会出现重复关键字。每一次插入都将变为根节点的子节点,例如根节点关键字为1,接下来插入的节点则变为根节点的右子节点。
public void insertNode(int key,String value){
Node node = new Node();
node.index = key;
node.data = value;
if(root == null){
root = node;
return;
}
//找到插入节点的位置
Node parent = root;
Node current = root;
while(true){
parent = current;
if(key == current.index){
return;
}
if(key < current.index){//左节点
current = current.leftNode;
if(current == null){//当前节点已经是叶子结点了
parent.leftNode = node;
return;
}
}else{
current = current.rightNode;
if(current == null){
parent.rightNode = node;
return;
}
}
}
}
遍历节点,中序遍历.
- 调用自身来遍历节点的左子树
- 访问这个节点
- 掉用自身来遍历节点的右子树
public void inOrder(Node localRoot) {
if (localRoot != null) {
inOrder(localRoot.leftNode);
System.out.println("索引:" + localRoot.index + ",值:" + localRoot.data);
inOrder(localRoot.rightNode);
}
}
删除节点,分三种情况:
- 删除节点没有子节点,那么将父节点的左节点或者是右节点设置为空
- 删除节点只有一个子节点,删除该节点后,该节点的子节点变为父节点的子节点,如果删除节点时父节点的左节点,那么父节点的左节点指向该节点的子节点,反之则右节点指向删除节点的子节点。
- 删除节点有两个字节点,删除了该节点后,则需要选择一个后继节点,并且还不破坏该二叉树的特性(左节点要小于右节点),一般是从删除节点的右节点中找到一个后继节点,而这个节点是右子树的最小值。
public boolean delete(int key) {
Node current = root;
Node parent = root;
boolean isLeftChild = true;
//找到被删除的节点,并标识该节点是否为左节点
while (current.index != key) {
parent = current;
if (key < current.index) {
isLeftChild = true;
current = current.leftNode;
} else {
isLeftChild = false;
current = current.rightNode;
}
if (current == null) {
return false;
}
}
//第一种情况,删除节点为子节点
if (current.leftNode == null && current.rightNode == null) {
if (current == root) {
root = null;
} else {
if (isLeftChild) {
parent.leftNode = null;
} else {
parent.rightNode = null;
}
}
} else if ((current.leftNode != null && current.rightNode == null) || (current.leftNode == null && current.rightNode != null)) {
//第二中情况,删除节点只包含一个子节点,则将子节点移动动当前节点中
if (current.rightNode == null) {//删除的节点的左节点有值,右节点为空
if (root == current) {
root = current.leftNode;
} else {
if (isLeftChild) {
parent.leftNode = current.leftNode;
} else {
parent.rightNode = current.leftNode;
}
}
} else {//删除的节点的右节点有值,左节点为空
if (root == current) {
root = current.rightNode;
} else {
if (isLeftChild) {
parent.leftNode = current.rightNode;
} else {
parent.rightNode = current.rightNode;
}
}
}
} else if (current.leftNode != null && current.rightNode != null) {
//第三种情况,删除节点中有左右两个节点
//找到后继节点
Node processer = processer(current);
if (current == root) {//删除是根节点,则
root = processer;
} else {
if (isLeftChild) {
parent.leftNode = processer;
} else {
parent.rightNode = processer;
}
}
//选中的节点的左节点与删除节点的左节点相连
processer.leftNode = current.leftNode;
}
return true;
}
//找到后继节点
private Node processer(Node delNode) {
Node parent = delNode;
Node success = delNode;
Node current = delNode.rightNode;
while (current != null) {
// 后继节点父节点首先保存后继节点的状态
parent = current;
success = current;
// 后继节点 不断的向左更新
current = current.leftNode;
}
// 假如我们找到的后继节点不直接是 要删除节点的右节点 而是在其右节点那条子树上面最小的一个节点
if (success != delNode.rightNode) {
//后继节点的父节点断开其与后继节点左边的引用,重新连接上后继节点的右子节点(因为后继节点是没有左子节点的,锁以要保存之前树的状态,还要把后继节点的右子节点处理一下,不管 其存在不存在)
parent.leftNode = success.rightNode;
// 这时候后继节点的右边已经空了 上一条语句已经将其给了自己父节点的左子节点 然后让后继节点的右边 连接要删除节点的右子树
success.rightNode = delNode.rightNode;
}
return success;
}
二叉查找树 Java实现的更多相关文章
- 数据结构实现(四)二叉查找树java实现
转载 http://www.cnblogs.com/CherishFX/p/4625382.html 二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树 ...
- 递归的二叉查找树Java实现
package practice; public class TestMain { public static void main(String[] args) { int[] ao = {50,18 ...
- 二叉查找树--java
package com.test.tree; public class BinarySearchTree<T extends Comparable<? super T>> { ...
- LeetCode96_Unique Binary Search Trees(求1到n这些节点能够组成多少种不同的二叉查找树) Java题解
题目: Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For e ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- 数据结构笔记--二叉查找树概述以及java代码实现
一些概念: 二叉查找树的重要性质:对于树中的每一个节点X,它的左子树任一节点的值均小于X,右子树上任意节点的值均大于X. 二叉查找树是java的TreeSet和TreeMap类实现的基础. 由于树的递 ...
- Java for LintCode 验证二叉查找树
给定一个二叉树,判断它是否是合法的二叉查找树(BST) 一棵BST定义为: 节点的左子树中的值要严格小于该节点的值. 节点的右子树中的值要严格大于该节点的值. 左右子树也必须是二叉查找树. ...
- 二叉查找树的Java实现
为了克服对树结构编程的恐惧感,决心自己实现一遍二叉查找树,以便掌握关于树结构编程的一些技巧和方法.以下是基本思路: [1] 关于容器与封装.封装,是一种非常重要的系统设计思想:无论是面向过程的函数,还 ...
- 二叉查找树(三)之 Java的实现
概要 在前面分别介绍了"二叉查找树的相关理论知识,然后给出了二叉查找树的C和C++实现版本".这一章写一写二叉查找树的Java实现版本. 目录 1. 二叉树查找树2. 二叉查找树的 ...
随机推荐
- 使用Postman验证TFS Rest API
概述 你可能已经了解到,TFS自2015版本发布以来,开始支持通过REST API的方式提供接口服务,第三方平台可以通过通用的HTTP协议访问TFS系统,获取数据.请求编译等.REST API在原有. ...
- javascript快速排序的思考
还记得三个月前,学习过快速排序,示例所讲的python快速排序十分易于理解,然而网上学习的c#的快速排序当时就懵逼的,现在已经全忘了,大概记得个思路 在学习完了一些高级的js方法后,今天用js模拟了p ...
- LayaAir从入门到放弃
我是一个小白程序员,准备开发小程序找了很久小程序开发引擎,看到白鹭和LayaAir .经过一番资料查找后被LayaAir高性能.接口简洁及社区活跃所吸引,然后选择了LayaAir. 开发过程中一般小问 ...
- 【转】4G18的低成本NA玩法
首先是要再次强调一次,4G18的缸径是76MM,冲程是87.5MM.属于典型的长冲程低转发动机! 这种设计的优点是比较适合市区走停的工作状况,省油. 如果要针对改装方案而言因为这种低转时便可输出大扭矩 ...
- HDU4622 Reincarnation
题目链接:戳我 因为对应的很多询问,所以我们一定要将每一种询问先处理出来,然后O(1)查询. 至于怎么处理出来子串的子串呢? 我们固定左端点,然后依次加入子串即可.然后统计的时候直接统计last那一个 ...
- Two Pointers-349. Intersection of Two Arrays
Given two arrays, write a function to compute their intersection. Example:Given nums1 = [1, 2, 2, 1] ...
- java 中什么是aop
AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入 ...
- Android 打开系统设置界面及相应的系统界面
方法 1 :startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); 方法 2:Intent intent = new Intent(&quo ...
- .Net - WebApi
WebApi C#进阶系列 - WebApi: WebApi 服务监控 + log4net:
- n层满k叉树总共有多少个节点
2叉树 1 3 7 对应公式为(2^n-1)/1 3叉树 1 4 13 对应公式为(3^n-1)/2 4叉树 1 5 21对应公式为(4^n-1)/3 ... n层k叉树,总共有(k^n-1)/k-1 ...