二叉树(Java实现)
一、常见用语
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实现)的更多相关文章
- 二叉树JAVA实现
为了克服对树结构编程的畏惧感和神秘感,下定决心将二叉树的大部分操作实现一遍,并希望能够掌握二叉树编程的一些常用技术和技巧.关于编程实现中的心得和总结,敬请期待!~ [1] 数据结构和表示: 二叉树的 ...
- 剑指Offer:面试题23——从上往下打印二叉树(java实现)
问题描述: 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 思路: 按照层次遍历的方法,使用队列辅助. 1.将根结点加入队列. 2.循环出队,打印当前元素,若该结点有左子树,则将其加入队列,若 ...
- 剑指offer【04】- 重建二叉树(java)
题目:重建二叉树 考点:树 题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6, ...
- 二叉树 Java 实现 前序遍历 中序遍历 后序遍历 层级遍历 获取叶节点 宽度 ,高度,队列实现二叉树遍历 求二叉树的最大距离
数据结构中一直对二叉树不是很了解,今天趁着这个时间整理一下 许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显 ...
- 数据结构之二叉树java实现
二叉树是一种非线性数据结构,属于树结构,最大的特点就是度为2,也就是每个节点只有一个左子树和一个右子树.二叉树的操作主要为创建,先序遍历,中序遍历,后序遍历.还有层次遍历.遍历有两种方式,一是采用递归 ...
- 算法笔记_189:历届试题 横向打印二叉树(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 二叉树可以用于排序.其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树. 当遇到空子树 ...
- 非递归遍历二叉树Java版的实现代码(没写层次遍历)
直接上代码呵呵,里面有注解 package www.com.leetcode.specificProblem; import java.util.ArrayList; import java.util ...
- 22.从上往下打印二叉树 Java
题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 解题思路 就是二叉树的层序遍历.借助一个队列就可以实现.使用两个队列一个存放节点,一个存放值.先将根节点加入到队列中,然后遍历队列中的 ...
- 前序遍历构造已知二叉树(Java)
public BiNode createBiTree() { Scanner input = new Scanner(System.in); int k = input.nextInt(); if(k ...
- 非递归遍历二叉树Java实现
2018-10-03 20:16:53 非递归遍历二叉树是使用堆栈来进行保存,个人推荐使用双while结构,完全按照遍历顺序来进行堆栈的操作,当然在前序和后序的遍历过程中还有其他的压栈流程. 一.Bi ...
随机推荐
- hdu 5073 Galaxy 数学 铜牌题
0.5 题意:有n(n<=5e4)个质点位于一维直线上,现在你可以任意移动其中k个质点,且移动到任意位置,设移动后的中心为e,求最小的I=(x[1]-e)^2+(x[2]-e)^2+(x[3]- ...
- shell tail 命令
#显示最后两行 tail -n - filename > newfilename #从开头显示到倒数第二行 head -n - filename > newfilename
- Nginx 499的问题
PHP 异步 HTTP 与 NGINX 499 PHP 异步 HTTP 在 PHP 代码中提交异步 HTTP 请求比较常用的方式是通过 fsockopen/fwrite/fclose 来实现,请参考如 ...
- CountDownLatch和CylicBarrier以及Semaphare你使用过吗
CountDownLatch 是什么 CountDownLatch的字面意思:倒计时 门栓 它的功能是:让一些线程阻塞直到另一些线程完成一系列操作后才唤醒. 它通过调用await方法让线程进入阻塞状态 ...
- 【APUE】第3章 文件I/O (2) 函数creat、lseek、read、write使用说明
1.函数creat 可以使用creat函数创建一个新文件. #include<fcntl.h> int creat(const char *path, mode_t mode); 返回值: ...
- spring boot打包以及centos下部署
spring boot打包以及部署 一.打包 springboot的打包方式有很多种.有打成war的,有打成jar的,也有直接提交到github,通过jekins进行打包部署的.这里主要介绍如何打成j ...
- Explain详解
explain语句用于查看某个查询语句具体使用了什么执行计划 执行输出各列详解 table 每条记录对应一个表的查询,如果是两表连接查询,就会有两条记录,table对应查询表名 id 查询语句可能是单 ...
- antd源码分析之——标签页(tabs 3.Tabs的滚动效果)
由于ant Tabs组件结构较复杂,共分三部分叙述,本文为目录中第三部分(高亮) 目录 一.组件结构 antd代码结构 rc-ant代码结构 1.组件树状结构 2.Context使用说明 3.rc-t ...
- 解决:安装jenkins时web界面出现jenkins实例似乎已离线问题
https://blog.51cto.com/8593714/2318144?tdsourcetag=s_pctim_aiomsg Windows下环境也可以解决:
- Sklearn-train_test_split随机划分训练集和测试集
klearn.model_selection.train_test_split随机划分训练集和测试集 官网文档:http://scikit-learn.org/stable/modules/gener ...