Java-数据结构:树

1. 概述

  • 定义:对一般的树加了约束:

  • 每个结点最多两棵子树,即二叉树中不存在 度大于2 的结点

  • 子树有 左右次序 之分

  • 有 5 种形态

二叉树的形态

  • 满二叉树完全二叉树(对满二叉树最底层,从右至左删除结点)

2. 重要特性

  • 二叉树,在第 i 层至多有 2i-1 个结点
  • 深度为 k 的二叉树至多有 2k-1 个结点
  • 高度(或深度)为 K 的完全二叉树至少有 2k-2 个 叶子结点
  • 非空二叉树的 叶子结点数 等于度为 2 的结点数加 1,即:n0 = n2 + 1

完全二叉树的 n1 只能是 0 或者 1

  • 一颗度为 m 的二叉树,度为 1 的结点为 n1,度为 2 的结点为 n2,… …,度为 m 的结点数为 nm,则叶子结点数:n0**= 1 + n2****+ 2n3****+…+ (m-1)nm**
  • 具有 n 个结点的完全二叉树,深度为 log2n + 1
  • 编号性质:n 个结点的完全二叉树(其深度为 log2n + 1),对各结点从上到下,从左到右依次编号(1~n)则:若 i 是某结点 a 的编号:
  • 如果 i 不等于 1,则 a 的双亲结点的编号为: ⌊ i/2 ⌋
  • 如果 2i ≤ n, 则 a 的左孩子编号为 2i;如果** 2i > n**, 则 a** 无左孩子**;
  • 如果** 2i + 1 ≤ n**, 则 a 的右孩子编号为** 2i + 1**;如果** 2i + 1> n**, 则 a** 无右孩子**;

三、二叉树的存储结构

1. 顺序存储

  • 用 数组 来存储数据元素
  • 从存储的角度来看,这种顺序存储结构,仅适用于 完全二叉树

因为在最坏的情况下,一个深度为 k 且只有 k 个结点的单支树( 树中不存在度为 2 的结点 ),却需要长度为 2k-1 的一维数组。

2. 链式存储

  • 以 链表 的形式,存储数据元素以及数据元素之间的关系。

链式存储


四、二叉树的遍历

1. 由遍历序列确定二叉树

  • 由 先序和中序,可以确定
  • 由 后序和中序,可以确定(但是注意后序最后一个为根下一个是右子树根
  • 由 层次和中序,可以确定

2. 根据遍历序列估计二叉树

  • 前序 遍历序列 和 后序 遍历序列 相同 的树:只有根结点
  • 前序 遍历 和 中序 遍历 相同 的二叉树:所有结点没有左子树(右单分支树
  • 中序 遍历 和 后序 遍历 相同 的二叉树:所有结点没有右子树(左单分支树)
  • 前序遍历 和 后序 遍历 相反 的二叉树:没有左子树或者没有右子树(只有一个叶子结点高度等于其结点数
  • 前序 遍历 和 中序 遍历 相反 的二叉树:所有结点没有右子树(左单分支树)
  • 中序 遍历 和 后序 遍历 相反 的二叉树:所有结点没有左子树(右单分支树)

3. 遍历和建树代码

  • 二叉树的建树
  • 深度优先遍历(先序,中序和后序)
  • 广度优先遍历(先序,后序)

/* BitTree.java */

package com.java.tree;

import java.util.LinkedList;
import java.util.Queue;

/**

  • Created by Jaco.Young.
  • 2018-06-13 18:26
    */
    public class BitTree {

//代表由先序和中序唯一确定的树的根结点
private TreeNode root;

/**

  • 提供给外部调用的方法
  • 字符数组pre表示先序遍历序列,mid表示中序遍历序列
    */
    public void build(char[] pre, char[] mid){
    //将创建树的根结点赋值给 root
    root = buildTree(pre,0, pre.length-1, mid, 0, mid.length-1);
    }

/**

  • 前提条件,树中不存在重复元素
  • 由先序遍历序列和中序遍历序列,构造二叉树的方法
  • 我们建树的过程总是将序列不断地分割成左子树、右子树
  • lPre、rPre和lMid、rMid,分别就表示要对先序和中序的哪一部分进行建树
    */
    private TreeNode buildTree(char[] pre, int lPre, int rPre, char[] mid, int lMid, int rMid){
    //在先序遍历序列中,找到当前这棵树的根结点
    char root = pre[lPre];

//在中序遍历序列中,根据先序中的根结点来查找在中序中的位置
int rootIndex = getRootIndex(mid, lMid, rMid, root);

//如果没有找到,说明所给的参数异常
if(rootIndex == -1){
throw new IllegalArgumentException(“Illegal Argument!”);
}

//计算当前这棵树,左右子树的个数
//整个中序序列:[左子树(lMid) root(rootIndex) 右子树(rMid)]
//左子树[lMid,rootIndex-1]
int lNum = rootIndex - lMid; //rootIndex-1 -lMid + 1
//右子树[rootIndex+1,rMid]
int rNum = rMid - rootIndex; //rMid - (rootIndex + 1) + 1

//开始构建当前根结点的左子树和右子树
//先构建左子树
TreeNode lchild; //作为左子树的根结点
//以当前结点为根的树,没有左子树
if(lNum == 0){
lchild = null;
}else{
//当前这个树的左子树,仍然是一棵树,递归构造这棵树的左子树
//设x为当前树先序中左子树最后一个元素的下标,则:x - (lpre + 1) = lNum
//得:x = lPre + lNum
lchild = buildTree(pre, lPre + 1, lPre+lNum, mid, lMid, rootIndex - 1);
}

//构建右子树
TreeNode rchild;
if(rNum == 0){
rchild = null;
}else{
//当前结点的右子树,仍然包含很多节点,需要递归的构造其右子树
rchild = buildTree(pre, lPre + lNum + 1, rPre, mid, rootIndex + 1, rMid);
}

//构造完整的二叉树
return new TreeNode(root,lchild,rchild);
}

//在中序遍历序列中,根据先序中的根结点来查找在中序中的位置
private int getRootIndex(char[] mid, int lMid, int rMid, char root) {
for(int i = lMid; i <= rMid; i++){
if(mid[i] == root){
return i;
}
}
return -1;
}

//二叉树每一个结点的结构
private class TreeNode{
//结点中存储的数据
char item;
//指向左孩子结点
TreeNode lChild;
//指向右孩子结点
TreeNode rChild;

//构造方法,完成初始化
public TreeNode(char item, TreeNode lChild, TreeNode rChild){
this.item = item;
this.lChild = lChild;
this.rChild = rChild;
}
}

//提供三个让外界调用的方法
public void preTraverse() {
preOrder(root);
}

public void midTraverse() {
midOrder(root);
}

public void postTraverse() {
postOrder(root);
}

//先序遍历 DLR
private void preOrder(TreeNode root) {
if( root != null) {
//先访问根节点
System.out.print(root.item + " ");
//递归访问左子树
preOrder(root.lChild);
//递归访问右子树
preOrder(root.rChild);
}
}

//中序遍历 LDR
private void midOrder(TreeNode root) {
if(root != null) {
//递归访问左子树
midOrder(root.lChild);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

[外链图片转存中…(img-XqMfoWdb-1712112278811)]

搜集费时费力,能看到此处的都是真爱!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值