一、常见用语

1、逻辑结构:描述数据之间逻辑上的相关关系。分为线性结构(如,字符串),和非线性结构(如,树,图)。

2、物理结构:描述数据的存储结构,分为顺序结构(如,数组)和链式结构。

3、结点的度:一个节点子树(即分支)个数。

4、叶结点:也称为终端节点,指度为0的节点。

5、树的深度(高度):树中结点层次的最大值。

6、有序树:子树有先后次序之分(我的理解就是左右节点次序不可以颠倒)。

7、同构:将节点适当重命名,即可得两颗完全相同的树

8、孩子节点:一个节点的直接后继节点。

9、双亲节点:一个节点的直接前驱节点。

二、二叉树中的概念

1、二叉树:满足以下两个条件的树称为二叉树

①节点的度不可以超过2

②节点的孩子节点次序不可颠倒

2、满二叉树:每层得节点数都是满的,即2i-1

3、完全二叉树:节点1~n分别对应于满二叉树的节点1~n

4、完全二叉树的性质:

(1)若节点序号为i(i>1),则其双亲节点序号为i/2。(这里是整除)

(2)若节点序号为i(i>=1),则其左子节点序号为2i。

(3)若节点序号为i (i>=1),则其右子节点序号为2i+1。

三、二叉树的操作

 1、二叉树节点的存储结构

public class TreeNode {
String data;
TreeNode LChild;
TreeNode RChild;
TreeNode(String data) {
this.data = data;
}
public String toString() {
return data;
}
}

 2、创建二叉树

使用前序遍历创建二叉树是比较合适,按照逻辑,总要先创建根节点在创建左右子树,总不能还没有创建根节点就把root.LChild传递出去吧。

private static String[] tree = {"A","B",".",".","C","D",".",".","."};
private static int i = 0;
//先序创建二叉树是比较合适的
TreeNode inOrderCreateBTree() {
TreeNode bt = null;
String s = tree[i++];
if(s == ".") {
return bt;
}else {
bt = new TreeNode(s);
bt.LChild = inOrderCreateBTree();
bt.RChild = inOrderCreateBTree();
return bt;
}
}

可以用非递归的方式建树,但是难度还是挺大的。

3、先序遍历

递归方式:

//递归法前序遍历二叉树
void preOrderPrintBTree(TreeNode bt) {
if(bt == null) {
System.out.print("." + " ");
}else {
System.out.print(bt + " ");
preOrderPrintBTree(bt.LChild);
preOrderPrintBTree(bt.RChild);
}
}

基于栈的非递归方式:

跟着思路写就好:事实上这就是一个代码的模板,三种遍历的代码在结构上都是差不多的。

①指针移到最左子孙节点,边移变打印,边入栈(入栈是为了保存双亲节点,以便访问右子树)。

while(p != null) {
System.out.print(p + " ");
stack.push(p);
p = p.LChild;
if(p == null) {
System.out.print("." + " ");
}
}

②栈不空,就出栈,p指针指向右子树。

if(!stack.isEmpty()) {
p = stack.pop();
p = p.RChild;
if(p == null) {
System.out.print("." + " ");
}
}

完整代码:

//基于栈的非递归法先序遍历二叉树
void preOrderPrintBTree1(TreeNode bt) {
if(bt == null) {
System.out.println("null tree");
}
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode p = bt;
while(!stack.isEmpty() || p != null) {
while(p != null) {
System.out.print(p + " ");
stack.push(p);
p = p.LChild;
if(p == null) {
System.out.print("." + " ");
}
}
if(!stack.isEmpty()) {
p = stack.pop();
p = p.RChild;
if(p == null) {
System.out.print("." + " ");
}
}
}
}

 4、中序遍历

递归法:

void inOrderPrint(TreeNode bt) {
if(bt == null) {
System.out.print("." + " ");
} else {
inOrderPrint(bt.LChild);
System.out.print(bt + " ");
inOrderPrint(bt.RChild);
}
}

非递归法:

前序遍历和中序遍历是查不多的,前者先打印后入栈,后者是先入栈后打印。

①p指针移到最左端,边移变入栈。

while(p != null) {
stack.push(p);
p = p.LChild;
}

②边出栈边打印,p指针指向右子树

if(!stack.isEmpty()) {
p = stack.pop();
System.out.print(p + " ");
}

完整代码:

void inOrderPrint1(TreeNode bt) {
if(bt == null) {
System.out.println(".");
}else {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode p = bt;
while(!stack.isEmpty() || p != null) {
while(p != null) {
stack.push(p);
p = p.LChild;
}
if(!stack.isEmpty()) {
p = stack.pop();
System.out.print(p + " ");
}
}
}
}

 5、后续遍历

递归:

void postOrderPrint(TreeNode bt) {
if(bt == null) {
System.out.print("." + " ");
}else {
postOrderPrint(bt.LChild);
postOrderPrint(bt.RChild);
System.out.print(bt + " ");
}
}

非递归:后续遍历的难点就在于要知道前一次访问的节点是左孩子还是右孩子,所以要设置一个前置指针pre。

①pcur指针移到最左端,边移边入栈

②pcur的有孩子被为null或者被访问过,则访问pcur,否则pcur要继续入栈,pcur指向其右孩子。

完整代码:

void postOrderPrint1(TreeNode bt) {
if(bt == null) {
System.out.print("." + " ");
}else {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode pcur = bt;
TreeNode pre = null;
while(pcur != null) {
stack.push(pcur);
pcur = pcur.LChild;
}
while(!stack.isEmpty()) {
pcur = stack.pop(); if(pcur.RChild == null || pcur.RChild == pre) {
System.out.print(pcur + " ");
pre = pcur;
}else {
stack.push(pcur);
pcur = pcur.RChild;
while(pcur != null) {
stack.push(pcur);
pcur = pcur.LChild;
}
}
}
}
}

 6、二叉树的其他操作

(1)打印叶子节点

//前序遍历打印叶子节点
public static void preprintLeaves(TreeNode bt) {
if(bt == null) { }else {
if(bt.LChild == null && bt.RChild == null) {
System.out.print(bt + " ");
}
preprintLeaves(bt.LChild);
preprintLeaves(bt.RChild);
}
}

(2)求树的深度

//先序遍历求二叉树的深度
public static int preTreeDepth(TreeNode bt, int h) {
if(bt != null) {
if(h > depth) depth = h;
preTreeDepth(bt.LChild,h+1);
preTreeDepth(bt.RChild,h+1);
}
return depth;
}
//后续遍历求二叉树深度
public static int postTreeDepth(TreeNode bt) {
int hl = 0;
int hr = 0;
int max = 0;
if(bt == null) {
return 0;
}else {
hl = postTreeDepth(bt.LChild);
hr = postTreeDepth(bt.RChild);
max = Math.max(hr, hl);
return max + 1;
}
}

二叉树(Java实现)的更多相关文章

  1. 二叉树JAVA实现

    为了克服对树结构编程的畏惧感和神秘感,下定决心将二叉树的大部分操作实现一遍,并希望能够掌握二叉树编程的一些常用技术和技巧.关于编程实现中的心得和总结,敬请期待!~ [1]  数据结构和表示: 二叉树的 ...

  2. 剑指Offer:面试题23——从上往下打印二叉树(java实现)

    问题描述: 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 思路: 按照层次遍历的方法,使用队列辅助. 1.将根结点加入队列. 2.循环出队,打印当前元素,若该结点有左子树,则将其加入队列,若 ...

  3. 剑指offer【04】- 重建二叉树(java)

    题目:重建二叉树 考点:树 题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6, ...

  4. 二叉树 Java 实现 前序遍历 中序遍历 后序遍历 层级遍历 获取叶节点 宽度 ,高度,队列实现二叉树遍历 求二叉树的最大距离

    数据结构中一直对二叉树不是很了解,今天趁着这个时间整理一下 许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显 ...

  5. 数据结构之二叉树java实现

    二叉树是一种非线性数据结构,属于树结构,最大的特点就是度为2,也就是每个节点只有一个左子树和一个右子树.二叉树的操作主要为创建,先序遍历,中序遍历,后序遍历.还有层次遍历.遍历有两种方式,一是采用递归 ...

  6. 算法笔记_189:历届试题 横向打印二叉树(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 二叉树可以用于排序.其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树. 当遇到空子树 ...

  7. 非递归遍历二叉树Java版的实现代码(没写层次遍历)

    直接上代码呵呵,里面有注解 package www.com.leetcode.specificProblem; import java.util.ArrayList; import java.util ...

  8. 22.从上往下打印二叉树 Java

    题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 解题思路 就是二叉树的层序遍历.借助一个队列就可以实现.使用两个队列一个存放节点,一个存放值.先将根节点加入到队列中,然后遍历队列中的 ...

  9. 前序遍历构造已知二叉树(Java)

    public BiNode createBiTree() { Scanner input = new Scanner(System.in); int k = input.nextInt(); if(k ...

  10. 非递归遍历二叉树Java实现

    2018-10-03 20:16:53 非递归遍历二叉树是使用堆栈来进行保存,个人推荐使用双while结构,完全按照遍历顺序来进行堆栈的操作,当然在前序和后序的遍历过程中还有其他的压栈流程. 一.Bi ...

随机推荐

  1. [python]有中文字符程序异常的解决方案

    一. 含有中文字符无法运行 在python3中用的是Unicode编码,Unicode号称万国码,可以向所有的编码进行兼容.不会出现这种问题. Python2中使用的是ASCII编码,会出现这种问题. ...

  2. Error creating bean with name 'persistenceExceptionTranslationPostProcessor' defined in class path resource [org/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfiguration

    dubbo 包和SpringBoot 冲突,注释就可以正常启动

  3. 焦虑的 BAT、不安的编程语言,揭秘程序员技术圈生存现状!

    [程序人生编者按]在迭代不休的技术圈中,仅在过去的一个月期间,我们见证了有史以来第一张黑洞照片的诞生:经历了为让人义愤填膺的 996:思考了作为程序员的年龄之槛:膜拜了技术大神的成长历程:追逐了如编程 ...

  4. shell [ ] 和 [[ ]] 区别

    [参考文章]:shell if [[ ]]和[ ]区别 || && [参考文章]:Shell test 命令 1. [ ] 和 test test  等同于 [ ] 可用于判断某个条件 ...

  5. 树状数组优化dp,一维排序,一维离散化

    #include<iostream> #include<cstdio> #include<algorithm> #include<vector> #in ...

  6. Flume-概述

    Flume 是 Cloudera 提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统.Flume 基于流式架构,灵活简单. Flume最主要的作用就是,实时读取服务器本地磁盘的数据, ...

  7. 操作系统 | 结合 CPU 理解一行 Java 代码是怎么执行的

    根据冯·诺依曼思想,计算机采用二进制作为数制基础,必须包含:运算器.控制器.存储设备,以及输入输出设备,如下图所示. 我们先来分析 CPU 的工作原理,现代 CPU 芯片中大都集成了,控制单元,运算单 ...

  8. CMDB项目

    实现功能 邮箱验证码注册 图片验证码登陆 生成验证码图片,前端请求图片: 获取验证码字符串,防入当前session: 获取前端用户名.密码.验证码: 判断验证码是否匹配,判断用户名和密码是否匹配. 设 ...

  9. Vim/gVim 中文显示为乱码的解决办法

    打开vimrc文件,在vim的安装目录下可以找到该文件,或在windows下是在vim/gvim下输入:edit $vim/_vimrc. 在文件的末尾添加一句 "set fileencod ...

  10. DISCUZ论坛各大功能模块入口文件介绍

    index.php 首页入口文件,这个文件相信大家都不陌生,小编就不具体介绍了. forum.php 论坛入口文件 portal.php 门户入口文件 group.php 群组入口文件 home.ph ...