Java数据结构(十)—— 树
树
树的概念和常用术语
常用术语
节点
根节点
父节点
子节点
叶子节点:没有子节点的节点
节点的权:节点的值
路径:节点A到节点B的路径
层
子树
树的高度:最大层数
森林:多颗子树构成森林
二叉树概念
每个节点最多只有两个子节点的树,叫二叉树
若该二叉树是满二叉树,节点数是2^n - 1,n为层数
完全二叉树:每一层节点都是连续的,即有子节点的父节点都有两个子节点
建立二叉树
思路分析
建立节点类
包含信息,左孩子指针,右孩子指针
二叉树的前中后序遍历算法
前序遍历
前序遍历
先输出父节点,再遍历左子树和右子树
中,左,右
思路分析
先输出当前节点,初始是root节点
如果左子节点不为空,则递归继续前序遍历
如果右子节点不为空,则递归继续前序遍历
中序遍历
中序遍历
先遍历左子树,在输出父节点,再遍历右子树
左,中,右
思路分析
如果左子节点不为空,则递归继续中序遍历
输出当前节点
如果右子节点不为空,则递归继续中序遍历
后序遍历
后序遍历
先遍历左子树,再遍历右子树,最后输出父节点
左,右,中
思路分析
如果左子节点不为空,则递归继续中序遍历
如果右子节点不为空,则递归继续中序遍历
输出当前节点
代码实现
package com.why.tree;
import java.sql.SQLOutput;
/**
* @Description TODO 前中后序遍历
* @Author why
* @Date 2020/11/4 16:49
* Version 1.0
**/
public class BinaryTreeDemo {
public static void main(String[] args) {
//创建二叉树
BinaryTree binaryTree = new BinaryTree();
//创建需要的节点
BNode root = new BNode(1,"why");
BNode node1 = new BNode(1,"cjl");
BNode node2 = new BNode(1,"wl");
BNode node3 = new BNode(1,"lrx");
//手动创建二叉树,后边以递归方式建立二叉树
root.setLeftChild(node1);
root.setRightChild(node2);
node2.setRightChild(node3);
//设置根节点
binaryTree.setRoot(root);
System.out.println("前序遍历:");
binaryTree.preOrder();
System.out.println();
System.out.println("中序遍历:");
binaryTree.midOrder();
System.out.println();
System.out.println("后序遍历:");
binaryTree.postOrder();
}
}
/**
* 定义二叉树
*/
class BinaryTree{
private BNode root;
public BNode getRoot() {
return root;
}
public void setRoot(BNode root) {
this.root = root;
}
/**
* 前序遍历
*/
public void preOrder(){
if (this.root != null){
this.root.preOrder();
}else {
System.out.println("二叉树为空");
}
}
/**
* 中序遍历
*/
public void midOrder(){
if (this.root != null){
this.root.midOrder();
}else {
System.out.println("二叉树为空");
}
}
/**
* 后序遍历
*/
public void postOrder(){
if (this.root != null){
this.root.postOrder();
}else {
System.out.println("二叉树为空");
}
}
}
/**
* 创建节点类
*/
class BNode{
private int id;
private String name;
private BNode leftChild;//左孩子节点
private BNode rightChild;//右孩子节点
public BNode(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BNode getLeftChild() {
return leftChild;
}
public void setLeftChild(BNode leftChild) {
this.leftChild = leftChild;
}
public BNode getRightChild() {
return rightChild;
}
public void setRightChild(BNode rightChild) {
this.rightChild = rightChild;
}
@Override
public String toString() {
return "BNode{" +
"id=" + id +
", name='" + name+
'}';
}
/**
* 前序遍历
*/
public void preOrder(){
//先输出当前(父节点)节点
System.out.println(this);
//递归左子树
if (this.leftChild != null){
this.leftChild.preOrder();
}
//递归右子树
if (this.rightChild != null){
this.rightChild.preOrder();
}
}
/**
* 中序遍历
*/
public void midOrder(){
//先中序遍历左子树
if (this.leftChild != null){
this.leftChild.midOrder();
}
//输出当前节点
System.out.println(this);
//中序遍历右子树
if (this.rightChild != null){
this.rightChild.midOrder();
}
}
/**
* 后序遍历
*/
public void postOrder(){
//后序遍历左子树
if (this.leftChild != null){
this.leftChild.preOrder();
}
//后序遍历右子树
if (this.rightChild != null){
this.rightChild.postOrder();
}
//输出当前节点
System.out.println(this);
}
}
二叉树的前中后序查找算法
前序查找
思路分析
和当前节点比较,若相等返回当前节点
若不相等
左子树不为空,左递归前序查找,找到节点返回
右子树不为空,右递归前序查找,找到节点返回,找不到显示没有搜索结果
代码实现
节点类
/**
* 前序查找
* @param no
* @return
*/
public BNode preSearch(int no){
if (this.getId() == no){//与当前节点比较,相等返回
return this;
}
//遍历左子树查找
BNode bNode = null;
if (this.leftChild != null){
bNode = this.leftChild.preSearch(no);
}
if (bNode != null){//左子树找到
return bNode;
}
//遍历右子树
if (this.rightChild != null){
bNode = this.rightChild.preSearch(no);
}
return bNode;
}
二叉树类
/**
* 前序查找
* @param no
*/
public void preSearch(int no){
if (this.root != null){
BNode bNode = this.root.preSearch(no);
if (bNode != null){
System.out.println(bNode);
}else {
System.out.println("未找到");
}
}else {
System.out.println("二叉树为空");
}
}
中序查找
思路分析
左子树不为空,左递归中序查找,找到节点返回
若未找到
和当前节点比较,若相等返回当前节点
右子树不为空,右递归中序查找,找到节点返回,找不到显示没有搜索结果
代码实现
节点类
/**
* 中序查找
* @param no
* @return
*/
public BNode midSearch(int no){
BNode bNode = null;
//遍历左子树
if (this.leftChild != null){
bNode = this.leftChild.midSearch(no);
}
if (bNode != null){
return bNode;
}
//与当前节点比较
if (this.getId() == no){
return this;
}
//遍历右子树
if (this.rightChild != null){
bNode = this.rightChild.midSearch(no);
}
return bNode;
}
二叉树类
/**
* 中序查找
* @param no
*/
public void midSearch(int no){
if (this.root != null){
BNode bNode = this.root.midSearch(no);
if (bNode != null){
System.out.println(bNode);
}else {
System.out.println("未找到");
}
}else {
System.out.println("二叉树为空");
}
}
后序查找
思路分析
左子树不为空,左递归后序查找,找到节点返回
若未找到
右子树不为空,右递归后序查找,找到节点返回
和当前节点比较,若相等返回当前节点,找不到显示没有搜索结果
代码实现
节点类
/**
* 后序查找
* @param no
* @return
*/
public BNode postSearch(int no){
BNode bNode = null;
//遍历左子树
if (this.leftChild != null){
bNode = this.leftChild.postSearch(no);
}
if (bNode != null){
return bNode;
}
//遍历右子树
if(this.rightChild != null){
bNode = this.rightChild.postSearch(no);
}
if (bNode != null){
return bNode;
}
//与当前节点比较
if (this.getId() == no){
bNode = this;
}
return bNode;
}
二叉树类
/**
* 后序查找
* @param no
*/
public void postSearch(int no){
if (this.root != null){
BNode bNode = this.root.postSearch(no);
if (bNode != null){
System.out.println(bNode);
}else {
System.out.println("未找到");
}
}else {
System.out.println("二叉树为空");
}
}
删除二叉树指定的节点
要求
若删除的是叶子节点,则删除该节点
如果删除的节点是非叶子节点,则删除该子树
思路
因为二叉树是单向的,所以判断 当前节点的子节点是否是待删除的节点
不能判断当前节点是不是需要删除的节点
如果当前节点的左孩子节点不为空,并且左孩子节点是待删除的节点将this.left置空,返回
如果当前节点的左孩子节点不为空,并且左孩子节点是待删除的节点将this.left置空,返回
如果上述步骤,没有删除节点,那么就需要向左/右子树递归删除
如果只有一个root节点,则等价于将二叉树指控
代码实现
BNode.java
/**
* 删除指定的节点
* @param no
*/
public void delete(int no){
if(this.getLeftChild() != null && this.getLeftChild().getId() == no){
this.setLeftChild(null);
return;
}
if (this.getRightChild() != null && this.getRightChild().getId() == no){
this.setRightChild(null);
return;
}
//向左子树递归
if (this.getRightChild() != null){
this.getLeftChild().delete(no);
}
//向右子树递归
if (this.getRightChild() != null){
this.getRightChild().delete(no);
}
}
BinaryTree.java
/**
* 根据id删除节点/子树
* @param no
*/
public void delete(int no){
if (this.root != null){
if(this.root.getId() == no){
this.root = null;
}else {
this.root.delete(no);
}
}else {
System.out.println("二叉树为空");
}
}
顺序存储二叉树
存储结构

要求
上图二叉树的节点,要求以数组的方式来存放[1,2,3,4,5,6,]
但是仍然能够以树的形式来遍历
顺序存储二叉树的特点
顺序二叉树通常只考虑完全二叉树
第n个元素的左孩子节点是2*n + 1
第n个元素的右子节点是2*n + 2
第n个元素的父节点为(n-1)/2
代码实现
package com.why.tree;
/**
* @Description TODO
* @Author why
* @Date 2020/11/12 15:14
* Version 1.0
**/
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7};
ArrBinaryTree abt = new ArrBinaryTree(arr);
// abt.preOrder();
// abt.midOrder();
abt.postOrder();
}
}
class ArrBinaryTree{
private int[] arr;
public ArrBinaryTree(int[] arr) {
this.arr = arr;
}
/**
* 顺序存储二叉树的前序遍历
* @param index 数组的下标
*/
public void preOrder(int index){
//如果数组为空,或者arr.length == 0
if (arr == null || arr.length == 0){
System.out.println("数组为空不能按照二叉树进行前序遍历");
}
//输出当前元素
System.out.println(arr[index]);
//向左递归遍历
if ((2 * index +1) < arr.length){
preOrder(2 * index +1);
}
//向右递归
if ((2 * index +2) < arr.length){
preOrder(2 * index +2);
}
}
/**
* 重载方法
*/
public void preOrder(){
this.preOrder(0);
}
/**
* 中序遍历
* @param index
*/
public void midOrder(int index){
if (arr == null || arr.length == 0){
System.out.println("数组为空");
}
//左递归
if ((2 * index + 1) < arr.length){
midOrder(2 * index + 1);
}
//输出当前元素
System.out.println(arr[index]);
if ((2 * index + 2) < arr.length){
midOrder(2 * index + 2);
}
}
public void midOrder(){
midOrder(0);
}
/**
* 后序遍历
* @param index
*/
public void postOrder(int index){
if (arr == null || arr.length == 0){
System.out.println("数组为空");
}
//左递归
if ((2 * index + 1) < arr.length){
postOrder(2 * index + 1);
}
//右递归
if ((2 * index + 2) < arr.length){
postOrder(2 * index + 2);
}
//输出当前元素
System.out.println(arr[index]);
}
public void postOrder(){
postOrder(0);
}
}
作用:堆排序用到二叉树的顺序存储
线索化二叉树
基本介绍
n个节点的二叉链表中含有n+1个空指针域。利用二叉链表中的空指针域,存放指向节点在某种遍历次序下的前驱和后继节点的指针(附加的指针叫线索)
加上了线索的二叉链表叫做线索链表,相应的二叉树称为线索二叉树(threaded BinaryTree)根据线索性质不同,线索二叉树可分为前序线索二叉树,中序线索二叉树,后序线索二叉树三种
一个节点的前一节点是前驱节点
一个节点的后一节点是后继结点
说明
当线索化二叉树后,Node节点的属性left和right,有如下情况:
left指向的是左子树,也可能指向前驱节点
right指向右子树也可能指向后继结点

代码实现
package com.why.tree;
import javax.swing.*;
/**
* @Description TODO
* @Author why
* @Date 2020/11/24 15:08
* Version 1.0
**/
public class ThreadBinaryTreeDemo {
public static void main(String[] args) {
//测试中序线索二叉树
int[] arr = {8,3,10,1,14,6};
Node root = new Node(1);
Node node1 = new Node(3);
Node node2 = new Node(6);
Node node3= new Node(8);
Node node4 = new Node(10);
Node node5 = new Node(14);
//手动创建二叉树
ThreadBinaryTree tbt = new ThreadBinaryTree(root);
root.setLeft(node1);
root.setRight(node2);
node1.setLeft(node3);
node1.setRight(node4);
node2.setLeft(node5);
tbt.midOrder();
tbt.threadedNodes();
//测试中序线索化是否正确,验证10号节点前驱后继节点是否正确
System.out.println("前驱:"+node4.getLeft());
System.out.println("后继:" + node4.getRight());
}
}
class ThreadBinaryTree{
//定义根节点
private Node root = new Node();
//为实现线索化,创建一个指向当前节点的前驱节点的指针
//递归进行线索化时,pre保留前一个节点
private Node pre = null;
public ThreadBinaryTree(Node root) {
this.root = root;
}
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
/**
* 判空
* @return
*/
boolean isEmpty(){
if (this.root == null){
return true;
}
return false;
}
/**
* 中序线索化
* @param node
*/
public void threadedNodes(Node node){
//如果node == null,不能线索化
if (node == null){
return;
}
//第一步,线索化左子树
threadedNodes(node.getLeft());
//第二步,线索化当前节点
//2.1.处理当前节点的前驱节点
if (node.getLeft() == null){
//当前节点的左指针指向前驱节点
node.setLeft(pre);
//修改当前节点的左指针类型
node.setLeftType(1);
}
//2.2.处理当前节点的后继结点,下一次处理,pre指向上一次node
if (pre != null && pre.getRight() == null){
//让前驱节点的右指针指向当前节点
pre.setRight(node);
//修改前驱节点的右指针类型
pre.setRightType(1);
}
//每处理一个节点后让当前节点是下一个节点的前驱节点
pre = node;
//第三步,线索化右子树
threadedNodes(node.getRight());
}
//重载
public void threadedNodes(){
threadedNodes(root);
}
/**
* 中序遍历
*/
public void midOrder(){
if (this.root != null){
this.root.midOrder();
}else {
System.out.println("二叉树为空");
}
}
}
//线索化二叉树节点
class Node{
private int no;//数值
private Node left;//左孩子节点 || 前驱节点
private Node right;//右孩子节点 || 后继结点
/**
* 说明:
* leftType == 0 表示指向左子树
* leftType == 1 表示指向前驱节点
*/
private int leftType;
/**
* 说明:
* rightType == 0 表示指向有子树
* rightType == 1 表示指向后继结点
*/
private int rightType;
public Node() {
}
public Node(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
public int getLeftType() {
return leftType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public int getRightType() {
return rightType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
'}';
}
public void midOrder(){
//遍历左子树
if (this.getLeft() != null){
this.left.midOrder();
}
//输出当前节点
System.out.println(this);
//遍历右子树
if (this.getRight() != null){
this.right.midOrder();
}
}
}遍历线索化二叉树
分析:由于线索化后各个节点的指向有变化,因此原来的遍历方式不能够再去使用
各个节点可以使用线性的方式遍历,无需使用递归,提高了遍历效率,遍历次序应和线索化方式相同
代码实现:
/**
* 中序遍历线索化二叉树
*/
public void midOrder(){
Node node = root;
while (node != null){
//存储当前便利的节,从root开始
//循环找到leftType == 1的节点
while (node.getLeftType() == 0){
node = node.getLeft();
}
//打印当前节点
System.out.println(node);
//如果当前节点的右指针指向的是后继节点就一直输出
while (node.getRightType() == 1){
//获取到当前节点的后继节点
node = node.getRight();
System.out.println(node);
}
//替换这个遍历的节点
node = node.getRight();
}
}
Java数据结构(十)—— 树的更多相关文章
- Java数据结构之树和二叉树(2)
从这里始将要继续进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来 ...
- Java数据结构之树和二叉树
从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...
- java数据结构之树
树定义和基本术语定义树(Tree)是n(n≥0)个结点的有限集T,并且当n>0时满足下列条件: (1)有且仅有一个特定的称为根(Root)的结点: (2)当n>1时,其余结 ...
- Java数据结构——AVL树
AVL树(平衡二叉树)定义 AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,并且拥有自平衡机制.在AV ...
- Java数据结构——字典树TRIE
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种. 典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计. 它的优点是:利用字符串的公共 ...
- JAVA数据结构--AVL树的实现
AVL树的定义 在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度都是.增 ...
- Java数据结构——2-3树
定义2-3树是平衡的3路查找树,其中2(2-node)是指拥有两个分支的节点,3(3-node)是指拥有三个分支的节点.B-树是一种平衡的多路查找树,2-3树属于b-树,其也同样具有B-树的性质,如m ...
- java数据结构-12树相关概念
一.树 1.概念: 包含n(n>=0)个结点的有穷集:树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节 ...
- Java数据结构和算法(十四)——堆
在Java数据结构和算法(五)——队列中我们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列可以用有序数组来实现 ...
- Java数据结构和算法 - 什么是2-3-4树
Q1: 什么是2-3-4树? A1: 在介绍2-3-4树之前,我们先说明二叉树和多叉树的概念. 二叉树:每个节点有一个数据项,最多有两个子节点. 多叉树:(multiway tree)允许每个节点有更 ...
随机推荐
- 晚间测试13 A. Dove 打扑克 vector +模拟
题目描述 分析 这道题比较关键的一点就是要看出最终牌数的种类数不会超过 \(\sqrt{n}\) 种 知道了这个性质我们就可以用 \(vector\) 维护一个有序的序列 \(vector\) 中存放 ...
- array_walk_recursive 地址引用报错的问题
今天看十八哥的视频,学习array_walk_recursive的用法,发现一直报错: PHP版本:5.6.19 代码界面: 报错界面: 查了很长时间,不知道什么问题,后来在网上终于找到原因所在: + ...
- STM32入门系列-GPIO概念介绍
GPIO(general purpose intput output)是通用输入输出端口的简称,可以通过软件来控制其输入和输出.STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯 ...
- PHP 将数组转换为JSON字符串<兼容中文>
1 /************************************************************** 2 * 3 * 使用特定function对数组中所有元素做处理 4 ...
- Learn day9 粘包\struct用法\hashlib校验\socketserver并发\模块引入\进程\join\守护进程
1.粘包现象 总结 : 导致黏包现象的两种情况 hello,worl d (1) 在发送端,发送数据太快,频繁发送 (2) 在接收端,接收数据太慢,延迟截取 # ### 服务端 import sock ...
- Scala-Mongodb入门之CRUD
Scala入门之Mongo增删改查 环境jdk1.8,scala2.13 使用sbt管理依赖,在build.sbt中添加依赖: libraryDependencies += "org.mon ...
- Java基础系列-单例的7种写法
原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755322.html 一.概述 Java中单例有7种写法,这个是在面试中经常被问到的内容,而且有时候 ...
- MySQL全面瓦解7:查询的过滤条件
概述 在实际的业务场景应用中,我们经常要根据业务条件获取并筛选出我们的目标数据.这个过程我们称之为数据查询的过滤.而过滤过程使用的各种条件(比如日期时间.用户.状态)是我们获取精准数据的必要步骤, 这 ...
- python详细图像仿射变换讲解
仿射变换简介 什么是放射变换 图像上的仿射变换, 其实就是图片中的一个像素点,通过某种变换,移动到另外一个地方. 从数学上来讲, 就是一个向量空间进行一次线形变换并加上平移向量, 从而变换到另外一个向 ...
- Pytest学习(六) - conftest.py结合接口自动化的举例使用
一.conftest.py作用 可以理解成存放fixture的配置文件 二.conftest.py配置fixture注意事项 pytest会默认读取conftest.py里面的所有fixture co ...