数据结构(Java描述)之二叉树
基础概念
二叉树(binary tree)是一棵树,其中每个结点都不能有多于两个儿子。
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;

二叉树的遍历
二叉树的遍历是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。二叉树的遍历方式有很多,主要有前序遍历,中序遍历,后序遍历。
前序遍历
前序遍历的规则是:若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树

中序遍历
中序遍历的规则是:若树为空,则空操作返回;否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。可以看到,如果是二叉排序树,中序遍历的结果就是个有序序列。

后序遍历
后序遍历的规则是:若树为空,则空操作返回;然后先遍历左子树,再遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。

删除结点
对于二叉排序树的其他操作,比如插入,遍历等,比较容易理解;而删除操作相对复杂。对于要删除的结点,有以下三种情况:
1.叶子结点;
2.仅有左子树或右子树的结点
3.左右子树都有结点
对于1(要删除结点为叶子结点)直接删除,即直接解除父节点的引用即可,对于第2种情况(要删除的结点仅有一个儿子),只需用子结点替换掉父节点即可;而对于要删除的结点有两个儿子的情况,比较常用处理逻辑为,在其子树中找寻一个结点来替换,而这个结点我们成为中序后继结点。

可以看到,我们找到的这个用来替换的结点,可以是删除结点的右子树的最小结点(6),也可以是其左子树的最大结点(4),这样可以保证替换后树的整体结构不用发生变化。为什么称为中序后继结点呢?我们来看下这棵树的中序遍历结果 1-2-3--5--7-8-9。可以很清晰的看到,其实要找的这个结点,可以是结点5的前驱或者后继。
代码实现
package treeDemo;
/**
* Created by chengxiao on 2017/02/12.
*/
public class BinaryTree {
//根节点
private Node root;
/**
* 树的结点
*/
private static class Node{
//数据域
private long data;
//左子结点
private Node leftChild;
//右子结点
private Node rightChild;
Node(long data){
this.data = data;
}
}
/**
* 插入结点
* @param data
*/
public void insert(long data){
Node newNode = new Node(data);
Node currNode = root;
Node parentNode;
//如果是空树
if(root == null){
root = newNode;
return;
}
while(true){
parentNode = currNode;
//向右搜寻
if(data > currNode.data){
currNode = currNode.rightChild;
if(currNode == null){
parentNode.rightChild = newNode;
return;
}
}else{
//向左搜寻
currNode = currNode.leftChild;
if(currNode == null){
parentNode.leftChild = newNode;
return;
}
}
}
}
/**
* 前序遍历
* @param currNode
*/
public void preOrder(Node currNode){
if(currNode == null){
return;
}
System.out.print(currNode.data+" ");
preOrder(currNode.leftChild);
preOrder(currNode.rightChild);
}
/**
* 中序遍历
* @param currNode
*/
public void inOrder(Node currNode){
if(currNode == null){
return;
}
inOrder(currNode.leftChild);
System.out.print(currNode.data+" ");
inOrder(currNode.rightChild);
}
/**
* 查找结点
* @param data
* @return
*/
public Node find(long data){
Node currNode = root;
while(currNode!=null){
if(data>currNode.data){
currNode = currNode.rightChild;
}else if(data<currNode.data){
currNode = currNode.leftChild;
}else{
return currNode;
}
}
return null;
}
/**
* 后序遍历
* @param currNode
*/
public void postOrder(Node currNode){
if(currNode == null){
return;
}
postOrder(currNode.leftChild);
postOrder(currNode.rightChild);
System.out.print(currNode.data+" ");
}
/**
* 删除结点 分为3种情况
* 1.叶子结点
* 2.该节点有一个子节点
* 3.该节点有二个子节点
* @param data
*/
public boolean delete(long data) throws Exception {
Node curr = root;
//保持一个父节点的引用
Node parent = curr;
//删除为左结点还是右界定啊
boolean isLeft = true;
while(curr != null && curr.data!=data){
parent = curr;
if(data > curr.data){
curr = curr.rightChild;
isLeft = false;
}else{
curr = curr.leftChild;
isLeft = true;
}
}
if(curr==null){
throw new Exception("要删除的结点不存在");
}
//第一种情况,要删除的结点为叶子结点
if(curr.leftChild == null && curr.rightChild == null){
if(curr == root){
root = null;
return true;
}
if(isLeft){
parent.leftChild = null;
}else{
parent.rightChild = null;
}
}else if(curr.leftChild == null){
//第二种情况,要删除的结点有一个子节点且是右子结点
if(curr == root){
root = curr.rightChild;
return true;
}
if(isLeft){
parent.leftChild = curr.rightChild;
}else{
parent.rightChild = curr.rightChild;
}
}else if(curr.rightChild == null){
//第二种情况,要删除的结点有一个子节点且是左子结点
if(curr == root){
root = curr.leftChild;
return true;
}
if(isLeft){
parent.leftChild = curr.leftChild;
}else{
parent.rightChild = curr.leftChild;
}
}else{
//第三种情况,也是最复杂的一种情况,要删除的结点有两个子节点,需要找寻中序后继结点
Node succeeder = getSucceeder(curr);
if(curr == root){
root = succeeder;
return true;
}
if(isLeft){
parent.leftChild = succeeder;
}else{
parent.rightChild = succeeder;
}
//当后继结点为删除结点的右子结点
succeeder.leftChild = curr.leftChild;
}
return true;
}
public Node getSucceeder(Node delNode){
Node succeeder = delNode;
Node parent = delNode;
Node currNode = delNode.rightChild;
//寻找后继结点
while(currNode != null){
parent = succeeder;
succeeder = currNode;
currNode = currNode.leftChild;
}
//如果后继结点不是要删除结点的右子结点
if(succeeder != delNode.rightChild){
parent.leftChild = succeeder.rightChild;
//将后继结点的左右子结点分别指向要删除结点的左右子节点
succeeder.leftChild = delNode.leftChild;
succeeder.rightChild = delNode.rightChild;
}
return succeeder;
}
public static void main(String []args) throws Exception {
BinaryTree binaryTree = new BinaryTree();
//插入操作
binaryTree.insert(5);
binaryTree.insert(2);
binaryTree.insert(8);
binaryTree.insert(1);
binaryTree.insert(3);
binaryTree.insert(6);
binaryTree.insert(10);
//前序遍历
System.out.println("前序遍历:");
binaryTree.preOrder(binaryTree.root);
System.out.println();
//中序遍历
System.out.println("中序遍历:");
binaryTree.inOrder(binaryTree.root);
System.out.println();
//后序遍历
System.out.println("后序遍历:");
binaryTree.postOrder(binaryTree.root);
System.out.println();
//查找结点
Node node = binaryTree.find(10);
System.out.println("找到结点,其值为:"+node.data);
//删除结点
binaryTree.delete(8);
System.out.print("删除结点8,中序遍历:");
binaryTree.preOrder(binaryTree.root);
}
}
执行结果
前序遍历: 5 2 1 3 8 6 10 中序遍历: 1 2 3 5 6 8 10 后序遍历: 1 3 2 6 10 8 5 找到结点,其值为:10 删除结点8,中序遍历:5 2 1 3 10 6
数据结构(Java描述)之二叉树的更多相关文章
- 数据结构(Java描述)之线性表
基础概念 数据结构:是相互之间存在一种或多种关系的数据元素的集合. 逻辑结构和物理结构 关于数据结构,我们可以从逻辑结构和物理结构这两个维度去描述 逻辑结构是数据对象中数据元素之间的关系,是从逻辑意义 ...
- 【Java】 大话数据结构(9) 树(二叉树、线索二叉树)
本文根据<大话数据结构>一书,对Java版的二叉树.线索二叉树进行了一定程度的实现. 另: 二叉排序树(二叉搜索树) 平衡二叉树(AVL树) 二叉树的性质 性质1:二叉树第i层上的结点数目 ...
- Java描述表达式求值的两种解法:双栈结构和二叉树
Java描述表达式求值的两种解法:双栈结构和二叉树 原题大意:表达式求值 求一个非负整数四则混合运算且含嵌套括号表达式的值.如: # 输入: 1+2*(6/2)-4 # 输出: 3.0 数据保证: 保 ...
- 数据结构(Java语言描述)-第一章:概述
第一章 概述 1.0 序言 自己为啥要学数据结构嘞,我觉得主要有以下三个原因: 前段时间在看并发编程时,发现aqs,corrunthashmap等底层都用到了数据结构,主要的有队列,还有链表,学习数据 ...
- 数据结构与算法 java描述 第一章 算法及其复杂度
目录 数据结构与算法 java描述 笔记 第一章 算法及其复杂度 算法的定义 算法性能的分析与评价 问题规模.运行时间及时间复杂度 渐进复杂度 大 O 记号 大Ω记号 Θ记号 空间复杂度 算法复杂度及 ...
- 利用Java实现表达式二叉树
(*^-^*) 什么是二叉树,这里不再介绍,可以自行百度:二叉树.在这里利用java实现“表达式二叉树”. 表达式二叉树的定义 第一步先要搞懂表达式二叉树是个什么东东?举个栗子,表达式:(a+b×(c ...
- python数据结构之树和二叉树(先序遍历、中序遍历和后序遍历)
python数据结构之树和二叉树(先序遍历.中序遍历和后序遍历) 树 树是\(n\)(\(n\ge 0\))个结点的有限集.在任意一棵非空树中,有且只有一个根结点. 二叉树是有限个元素的集合,该集合或 ...
- Java递归方法遍历二叉树的代码
将内容过程中经常用的内容做个记录,如下内容内容是关于Java递归方法遍历二叉树的内容. package com.wzs; public class TestBinaryTree { public st ...
- Java实现查找二叉树&C++的做法
写了个Java的查找二叉树,用递归做的,不用递归的还没弄出来.先贴一下.回头再研究. BTreeTest.java: public class BTreeTest{ class Node{ Node ...
- 纯数据结构Java实现(5/11)(Set&Map)
纯数据结构Java实现(5/11)(Set&Map) Set 和 Map 都是抽象或者高级数据结构,至于底层是采用树还是散列则根据需要而定. 可以细想一下 TreeMap/HashMap, T ...
随机推荐
- (简单) POJ 3087 Shuffle'm Up,枚举。
Description A common pastime for poker players at a poker table is to shuffle stacks of chips. Shuff ...
- 15、手把手教你Extjs5(十五)各种Grid列的自定义渲染
Grid各列已经能够展示出来了.列的类型包括字符型,整型,浮点型,货币型,百分比型,日期型和布尔型,我自定义了各种类型的渲染样式: 1.整型:标题栏居中,数值靠右显示,正数颜色为蓝色,负数颜色为红色, ...
- MySQL 同步状态
Exec_Master_Log_Pos: The position of the last event executed by the SQL thread from the master's bin ...
- ARM的启动代码(1):介绍(转)
源:ARM的启动代码(1):介绍 很多朋友搞嵌入式,写起代码来一点问题没有,到最后上板子调试的时候,挂了.究其原因,还是对芯片的启动地址.启动方式.bootloader和操作系统的衔接出了问题.今天就 ...
- IOS开发-OC学习-常用功能代码片段整理
IOS开发-OC学习-常用功能代码片段整理 IOS开发中会频繁用到一些代码段,用来实现一些固定的功能.比如在文本框中输入完后要让键盘收回,这个需要用一个简单的让文本框失去第一响应者的身份来完成.或者是 ...
- php小知识。
合并数组的2个方式区别 1)键名为数字时,array_merge()不会覆盖掉原来的值,但+合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉(不是覆盖) 2)键 ...
- RecyclerView学习笔记
主要方法: setLayoutManager:设置显示方式,有LinearLayoutManager(像ListView一样的纵向列表布局),有GridLayoutManager(网格布局),有Sta ...
- 中国产品众筹NO.1诞生
中国产品众筹NO.1诞生 淘宝众筹打响新拐点之战 http://bbs.taobao.com/catalog/thread/508895-317240623.htm?spm=1.7274553.199 ...
- 谈谈如何从Apache官网扒文档
学习java的猴子至少要会看文档, 一.从Apache官网下载文档 进入官网--components--例如点击FileUpload--点击最下面browser download area--点击bi ...
- dp中表示无限取的写法
dp[i][j]=dp[i-a[i][j]+dp[i-a[i]][j-1]; 其中前一项j不变表示j这一项无限取