java--二叉树解析及基本实现
一.二叉树的结构
在进行链表结构开发的过程之中,会发现所有的数据按照首尾相连的状态进行保存,那么 在进行数据查询时为了判断数据是否存在,这种情况下它所面对的时间复杂度就是"O(n)",如果说它现在的数据量比较小(<30)是不会对性能造成什么影响的,而一旦保存的数据量很大,这个时候时间复杂度就会严重损耗程序的运行性能,那么对于数据的存储结构就必须发生改变,应该尽可能的减少检索次数为出发点进行设计.对于现在的数据结构而言,最好的性能就是"O(logn)",现在想要实现它,就可以使用二叉树的结构来完成.
如果想要实现一颗树结构的定义,那么就需要去考虑数据的存储形式,在二叉树的实现中,基本原理如下:取第一个保存的数据为根节点,当比根节点小或相等的数据需要放在根的左子树,而大于节点的数据要放在该节点的右子树.同时,在每一个树节点中需要保存的东西有如下:父节点,数据,左子树,右子树
--当要进行数据检索时,此时就需要进行每个节点的判断,例如现在我们要查找数据23,那么我们可以知道23比25小,那么查询25的左子树,而25的左子树为20比数据23小,则查询他的右子树,其右子树23就是我们所需要的数据.其时间复杂度为O(logn).
--对于二叉树的查询,也有三种形式,分别为:前序遍历(根-左-右),中序遍历(左-根-右),后序遍历(左-右-根),以中序遍历为例,则以上的数据在中序遍历的时候最终的结果就是(10,18,20,23,25,40,50,100),可以发现二叉树中的内容全部都是排序的结果.
二.二叉树的基础实现
二叉树实现的关键问题在于数据的保存,而且数据由于牵扯到对象比较的问题,那么一定要有比较器的支持,而首选的比较器就是Comparable,以Person数据为例:
package 常用类库.二叉树的实现; import javax.jws.Oneway;
import java.lang.reflect.Array;
import java.util.Arrays; /**
* @author : S K Y
* @version :0.0.1
*/
class Person implements Comparable<Person> {
private String name;
private int age; public Person() {
} public Person(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public int compareTo(Person o) {
return this.age - o.age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
} class BinaryTree<T extends Comparable<T>> {
private class Node {
private Comparable<T> data; //存放Comparable,可以比较大小
private Node parent; //存放父节点
private Node left; //保存左子树
private Node right; //保存右子树 public Node(Comparable<T> data) { //构造方式直接实现数据的存储
this.data = data;
} /**
* 实现节点数据的适当位置的存储
*
* @param newNode 创建的新节点
*/
void addNode(Node newNode) {
if (newNode.data.compareTo((T) this.data) <= 0) { //比当前的节点小
if (this.left == null) { //没有左子树,进行保存
this.left = newNode;
newNode.parent = this; //保存父节点
} else { //需要向左边继续判断
this.left.addNode(newNode); //继续向下判断
}
} else { //比根节点的数据要大
if (this.right == null) { //没有右子树
this.right = newNode;
newNode.parent = this; //保存父节点
} else {
this.right.addNode(newNode); //继续向下进行
}
}
} /**
* 实现所有数据的获取处理,按照中序遍历的形式来完成
*/
void toArrayNode() {
if (this.left != null) { //存在左子树
this.left.toArrayNode(); //递归调用
}
BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;
if (this.right != null) {
this.right.toArrayNode();
}
} } /*===========以下是二叉树的功能实现=============*/
private Node root; //保存的根节点
private int count; //保存数据个数
private Object[] returnData; //返回的数据
private int foot = 0; //脚标控制 /**
* 进行数据的增加
*
* @param data 需要保存的数据
* @throws NullPointerException 保存的数据不允许为空
*/
public void add(Comparable<T> data) {
if (data == null) {
throw new NullPointerException("保存的数据不允许为空");
}
//所有的数据本身不具备有节点关系的匹配,那么一定要将其包装在Node类之中
Node newNode = new Node(data); //保存节点
if (this.root == null) { //表名此时没有根节点,那么第一个保存的数据将作为根节点
this.root = newNode;
} else { //需要将其保存到一个合适的节点
this.root.addNode(newNode);
}
count++;
} /**
* 以对象数组的形式返回数据,如果没有数据则返回null
*
* @return 全部数据
*/
public Object[] toArray() {
if (this.count == 0) return null;
this.foot = 0; //脚标清零
this.returnData = new Object[count];
this.root.toArrayNode();
return returnData;
} } public class MyBinaryTree {
public static void main(String[] args) {
BinaryTree<Person> tree = new BinaryTree<>();
tree.add(new Person("小红", 20));
tree.add(new Person("小光", 80));
tree.add(new Person("小亮", 40));
tree.add(new Person("小龙", 25));
tree.add(new Person("小C", 77));
tree.add(new Person("小D", 66));
tree.add(new Person("小九", 35));
tree.add(new Person("小Q", 54));
Object[] objects = tree.toArray();
System.out.println(Arrays.toString(objects));
}
}
--运行结果
[Person{name='小红', age=20}, Person{name='小龙', age=25}, Person{name='小九', age=35}, Person{name='小亮', age=40}, Person{name='小Q', age=54}, Person{name='小D', age=66}, Person{name='小C', age=77}, Person{name='小光', age=80}]
Process finished with exit code 0
--在以上的代码实现中采用了递归算法的操作,采用递归算法,相对而言其代码更加的简介明了,但是此时在进行数据添加的时候,只是实现了节点关系的保存,而这种关系保存后的结果就是所有的数据都是有序排列的.
三.数据删除
二叉树的数据删除操作是非常复杂的,因为在进行数据删除的时候需要考虑的情况是比较多的:
--1.如果删除的节点没有子节点,那么直接删除该节点即可
--2.如果待删除节点只有一个子节点,那么删除该节点之后,考虑两种情况的分析:
a.只有一个左子树:将其左子树放置于原来父节点的位置
b.只有一个右子树:也是将其右子树放置于原来父节点的位置
--3.如果删除节点存在两个子节点,那么删除该节点,首先需要找到当前节点的后继节点,这个后继节点就是其右子树的左侧叶子节点(及该节点下的最后一个左子树)

--具体的代码实现
package 常用类库.二叉树的实现; import java.util.Arrays; /**
* @author : S K Y
* @version :0.0.1
*/
class Person implements Comparable<Person> {
private String name;
private int age; public Person() {
} public Person(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public int compareTo(Person o) {
return this.age - o.age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
} class BinaryTree<T extends Comparable<T>> {
private class Node {
private Comparable<T> data; //存放Comparable,可以比较大小
private Node parent; //存放父节点
private Node left; //保存左子树
private Node right; //保存右子树 public Node(Comparable<T> data) { //构造方式直接实现数据的存储
this.data = data;
} /**
* 实现节点数据的适当位置的存储
*
* @param newNode 创建的新节点
*/
void addNode(Node newNode) {
if (newNode.data.compareTo((T) this.data) <= 0) { //比当前的节点小
if (this.left == null) { //没有左子树,进行保存
this.left = newNode;
newNode.parent = this; //保存父节点
} else { //需要向左边继续判断
this.left.addNode(newNode); //继续向下判断
}
} else { //比根节点的数据要大
if (this.right == null) { //没有右子树
this.right = newNode;
newNode.parent = this; //保存父节点
} else {
this.right.addNode(newNode); //继续向下进行
}
}
} /**
* 实现所有数据的获取处理,按照中序遍历的形式来完成
*/
void toArrayNode() {
if (this.left != null) { //存在左子树
this.left.toArrayNode(); //递归调用
}
System.out.println(foot + " " + this.data + " parent:" + this.parent + " left:" + this.left + " right:" + this.right);
BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;
if (this.right != null) {
this.right.toArrayNode();
}
} @Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
} /*===========以下是二叉树的功能实现=============*/
private Node root; //保存的根节点
private int count; //保存数据个数
private Object[] returnData; //返回的数据
private int foot = 0; //脚标控制 /**
* 进行数据的增加
*
* @param data 需要保存的数据
* @throws NullPointerException 保存的数据不允许为空
*/
public void add(Comparable<T> data) {
if (data == null) {
throw new NullPointerException("保存的数据不允许为空");
}
//所有的数据本身不具备有节点关系的匹配,那么一定要将其包装在Node类之中
Node newNode = new Node(data); //保存节点
if (this.root == null) { //表名此时没有根节点,那么第一个保存的数据将作为根节点
this.root = newNode;
} else { //需要将其保存到一个合适的节点
this.root.addNode(newNode);
}
count++;
} /**
* 返回树中当前的节点,如果存在
*
* @param data 所需要在树中获取节点的对象
* @return 书中的当前节点, 如果不存在, 则返回null
*/
private Node getNode(Comparable<T> data) {
Node compareNode = BinaryTree.this.root; //当前比较的Node节点
int i; //当前的比较结果
while ((i = data.compareTo((T) compareNode.data)) != 0) {
if (i < 0) { //当前节点比此节点小
compareNode = compareNode.left;
} else { //当前节点比此节点大
compareNode = compareNode.right;
}
if (compareNode == null) return null; //不存在此节点,跳出循环,说明未找到数据
}
return compareNode;
} /**
* 判断当前节点是否存在
*
* @param data 需要判断的加节点
* @return 如果当前节点存在则返回true, 不存在则返回false
* @throws NullPointerException 查询的数据不允许为空
*/
public boolean contains(Comparable<T> data) {
if (data == null) return false; //当前对象为空
if (this.count == 0) return false; //当前不存在数据
return getNode(data) != null;
} /**
* 执行节点的删除处理
*
* @param data 需要删除的节点数据
*/
public void remove(Comparable<T> data) {
if (this.contains(data)) { //要删除的数据存在
//首先需要找到要删除的节点
Node removeNode = this.getNode(data);
if (removeNode.left == null && removeNode.right == null) { //情况1:当前节点不存在子节点
//此时只要断开该删除节点的连接即可
if (removeNode.equals(removeNode.parent.left)) {
removeNode.parent.left = null;
} else {
removeNode.parent.right = null;
}
removeNode.parent = null; //断开删除节点的引用
} else if (removeNode.left == null) { //此时说明只存在right子树
if (removeNode.equals(removeNode.parent.left)) {
removeNode.parent.left = removeNode.right;
} else {
removeNode.parent.right = removeNode.right;
}
removeNode.right.parent = removeNode.parent;
removeNode.parent = null;
} else if (removeNode.right == null) { //此时说明只存在left子树
if (removeNode.equals(removeNode.parent.left)) {
removeNode.parent.left = removeNode.left;
} else {
removeNode.parent.right = removeNode.left;
}
removeNode.left.parent = removeNode.parent;
removeNode.parent = null;
} else { //两边都有节点
Node needMoveNode = removeNode.right; //所需移动的节点
System.out.println("needMoveNode: " + needMoveNode.data);
while (needMoveNode.left != null) {
needMoveNode = needMoveNode.left;
} //此时已经获取删除节点的最小左节点,需要将其替代原来的节点
//考虑删除节点的右节点不存在左节点的情况,及删除节点的右节点就是最终的needMoveNode
if (needMoveNode.equals(needMoveNode.parent.right)) {
needMoveNode.parent.right = needMoveNode.right;
} else {
needMoveNode.parent.left = needMoveNode.right;
}
//替换节点的数据内容
removeNode.data = needMoveNode.data;
//断开needMoveNode的连接
needMoveNode.parent = null; }
this.count--;
}
} /**
* 以对象数组的形式返回数据,如果没有数据则返回null
*
* @return 全部数据
*/
public Object[] toArray() {
if (this.count == 0) return null;
this.foot = 0; //脚标清零
System.out.println("count: " + count);
this.returnData = new Object[count];
this.root.toArrayNode();
return returnData;
} } public class MyBinaryTree {
public static void main(String[] args) {
//为了验证算法结构的准确性,将其内容设置为与图示相同
BinaryTree<Person> tree = new BinaryTree<>();
tree.add(new Person("小红", 25));
tree.add(new Person("小光", 20));
tree.add(new Person("小亮", 40));
tree.add(new Person("小龙", 18));
tree.add(new Person("小C", 23));
tree.add(new Person("小D", 50));
tree.add(new Person("小九", 10));
tree.add(new Person("小Q", 22));
tree.add(new Person("小Q", 24));
tree.add(new Person("小Q", 100));
Object[] objects = tree.toArray();
System.out.println(Arrays.toString(objects));
//删除23节点
System.out.println("=======删除22节点========");
tree.remove(new Person("小Q", 22));
System.out.println(Arrays.toString(tree.toArray()));
System.out.println("=======删除18节点========");
tree.add(new Person("小Q", 22));
tree.remove(new Person("小龙", 18));
System.out.println(Arrays.toString(tree.toArray()));
System.out.println("=======删除50节点========");
tree.add(new Person("小龙", 18));
tree.remove(new Person("小D", 50));
System.out.println(Arrays.toString(tree.toArray()));
System.out.println("=======删除23节点========");
tree.add(new Person("小D", 50));
tree.remove(new Person("小C", 23));
System.out.println(Arrays.toString(tree.toArray()));
System.out.println("=======删除20节点========");
tree.add(new Person("小C", 23));
tree.remove(new Person("小光", 20));
System.out.println(Arrays.toString(tree.toArray()));
System.out.println("=======删除25根节点========");
tree.add(new Person("小光", 20));
tree.remove(new Person("小红", 25));
System.out.println(Arrays.toString(tree.toArray()));
}
}
--可以发现这种树结构的删除操作是非常繁琐的,所以如果不是必须的情况下不建议使用删除
java--二叉树解析及基本实现的更多相关文章
- Java Sax解析
一. Java Sax解析是按照xml文件的顺序一步一步的来解析,在解析xml文件之前,我们要先了解xml文件的节点的种类,一种是ElementNode,一种是TextNode.如下面的这段boo ...
- Java XML解析工具 dom4j介绍及使用实例
Java XML解析工具 dom4j介绍及使用实例 dom4j介绍 dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory ...
- Java泛型解析(03):虚拟机运行泛型代码
Java泛型解析(03):虚拟机运行泛型代码 Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的 ...
- java socket解析和发送二进制报文工具(附java和C++转化问题)
解析: 首先是读取字节: /** * 读取输入流中指定字节的长度 * <p/> * 输入流 * * @param length 指定长度 * @return 指定长度的字节数组 */ pu ...
- Java XML解析器
使用Apache Xerces解析XML文档 一.技术概述 在用Java解析XML时候,一般都使用现成XML解析器来完成,自己编码解析是一件很棘手的问题,对程序员要求很高,一般也没有专业厂商或者开源组 ...
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...
- java基础解析系列(六)---深入注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...
- java基础解析系列(七)---ThreadLocal原理分析
java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...
- java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...
随机推荐
- Linux磁盘分区实例演示
一直觉得Linux的分区操作很高大上,之前在物理机上装的Linux,所以不敢随便乱来,今天重回Windows,下面利用Windows上的Linux虚拟机实际操作一次磁盘分区,要求:1G的硬盘分成2个2 ...
- html-mailto
MailTo 属性 mailto 属性可以设置到a 标签和form 标签中 例如: <a href="mailto:****@qq.com">send mail< ...
- day64--pymysql模块的使用、视图、触发器、函数、存储过程、事务
一.pymysql的下载和使用 (一)pymysql模块的下载:pip3 install pymysql # 实现:使用Python实现用户登录,如果用户存在则登录成功(假设该用户已在数据库中) im ...
- C#学习大纲
一.C#: 1.进制转换 2.vs界面内容 熟悉软件 3.数据类型 :引用类型 值类型 4.变量 (存储数据)一个变量存储一个数据 5.类型转换 6.运算符:算数运 ...
- HDU-5378 概率DP
题意:给定一棵有n个节点的树,现在要给节点附1~n的权值(各节点权值不能相同),一棵子树的领袖就是子树中权值最大的节点,问有多少种分配方案使得最后有恰好K个领袖. 解法:这道题一看以为是树上的计数问题 ...
- 2019CCPC网络预选赛 1003 K-th occurrence 后缀自动机 + 二分 + 主席树
题意:给你一个长度为n的字符串,有m次询问,每次询问l到r的子串在原串中第k次出现的位置,如果没有输出-1.n, m均为1e5级别. 思路:后悔没学后缀数组QAQ,其实只要学过后缀数组这个题还是比较好 ...
- MFC程序执行过程剖析(转)
一 MFC程序执行过程剖析 1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用 ...
- Redis服务器中有75%受到恶意软件感染
尽管由于配置错误的服务器和应用程序而导致新的网络攻击不断出现,但人们仍然忽略安全警告. 近两个月前,中国知名黑客组织东方联盟研究人员警告说,一项针对开放Redis服务器的大规模恶意软件活动现在已经发展 ...
- hive中为分区表增加字段需要注意默认不会修改已有分区的字段,导致查询时新增字段为null
若向hive表添加字段,通常会使用下面这种语句 alter table default.testparquet add columns(c8 string); 但是对于分区表来说, 1. 若新建的分区 ...
- python3.x运行的坑:AttributeError: 'str' object has no attribute 'decode'
1.Python3.x和Python2.X版本有一些区别,我遇到了两个问题如下: a.第一个报:mysqlclient 1.3版本不对: 解决办法:注释掉这行即可: b.第二个报:字符集的问题: 报错 ...