知道二叉树在计算机里面是怎么存储的,有顺序存储结构,采用数组存储,也有链式存储结构,采用二叉链表,或者采用三叉链表,那采用
链式存储结构更多一点,下面我们会讲二叉树在计算机里的实现,就是链式存储结构的二叉链表为例来进行讲解的,讲这个具体实现之前呢,
我们要知道一下二叉树的遍历策略,
什么叫遍历?
大家想一下,如果是一个线性表的话,他的遍历是很简单的,怎么简单呢,比如说一个数组,来个for循环就遍历完了,链表的遍历怎么来遍历,
也是一个for循环,然后我们动指针就可以了,让指针指向下一个节点,直到末尾,它是一条线顺藤摸瓜,都可以把所有的数据遍历出来,
对于树来说就比较复杂了,这里面可是有好几条线啊,你怎么采用一种方式把里面的每个节点都走一遍,并且只走一遍,这就叫遍历,
遍历的单词叫Traverse:
1. 就是按照顺序访问树的所有节点,并且每个节点恰好访问一次,遍历其实是下面很多操作的一个基础,遍历相当于我们都能找到每个节点
2. 下面就可以查询,我按照我走的策略,看看比较这个值,看看是不是我找的,或者我要更新,我要把2这个值更新成5,遍历是后面很多操作的
一个基础,因为遍历我们会得到这样的一个数列,就好比是一个线性结构了,就像我们的线性表一样,这就是一个线性结构了,所以我们遍历呢
怎么办呢,也可以看成是认为的把非线性结构线性化,树是非线性结构的,就变成他了,关键点什么叫遍历,强调一下,非线性结构的线性化,
它是很多操作的基础,我们怎么进行遍历呢,我们把整个二叉树看成三部分,根,左子树和右子树,那如果按照这个来遍历的话,他就有几种排列
组合方式,到底是先做还是先右呢,如果三种任意排列的话,就有几种啊,他有6种,如果我们再规定一下,必须先遍历左子树,再遍历右子树,
一下子就减掉三个,这样一来,在这个前提下,规定先遍历左子树,再遍历右子树,这么一来就是三种方式,怎么会有三种方式呢,
1. 先遍历左子树,再遍历右子树,请问根要放在哪里,根你当然可以放到最前面了,这个叫先根遍历,或者叫先序遍历,
先遍历根,再遍历左子树,再遍历右子树
2. 还有一个我把根放在中间,这个叫中序或者中根遍历
3. 还有一个我把根放在最后,这个叫后续遍历
我们树的遍历主要有三种遍历,怎么这么复杂啊,我先第一层,再第二层,再第三层,再第四层,都遍历了一下,可以吗,也可以
这叫按照层次遍历,但是这种遍历在实际开发中呢,使用的并不多,更多的还是使用上面的一种遍历,并且上面这三种,因为
我们的树是采用递归遍历的,这是根,这是左子树,这是右子树,对于右子树来说他又有根,又有左子树又有右子树,它是递归
定律的,所以我们这种遍历的话,先根再左子树再右子树,他也是递归的,所以这三种遍历,我们都可以采用递归来实现,
递归有什么好处啊,编码简单,一会我们就能见到了,可以采用递归办法我们可以不用递归,不用递归代码就比较复杂了,
真的很复杂吗,一会大家就知道了,按层次遍历,只能按照非递归的方式,层次遍历没有递归,这个明确一下
我们先看一下遍历的算法,什么叫做先序,中序,后序,我们先写一下,就是这棵树,我能采用不同的算法,遍历一下,
首先要明白这个遍历的思路是什么,然后再用代码给大家实现,我们要进行遍历了,首先我们要进行先序遍历,
然后我们这边要进行中序遍历,这个叫后序或者后根遍历,都是可以的,
1. 首先我们的先序遍历是什么意思,先是根,然后是左子树,然后是右子树,是这么来的,根就是一个节点,左子树,
右子树就是多个节点了,所以我们应该怎么办,画一个图,先遍历根的,第一步先遍历根,然后该谁了,然后左子树,
然后右子树,虽然我不知道下面的顺序会是什么,但是我敢保证,肯定是先有4和5,先把4和5遍历出来,才有可能
遍历这一块,然后就遍历左子树,再遍历右子树了,对于左子树和右子树我们该怎么遍历啊,重复刚才的内容,
怎么重复呢,这个4是根,然后它有没有左啊,没有,它有没有右啊,有,这我们就进行遍历了,我们怎么来进行遍历,
左没有,没有就遍历右,右是5,下面该右子树了,右子树我们该怎么办,右子树和刚才的一样,先根,再左,然后再右,
先是2,然后是3,再然后就稍微等一下,再然后又是一棵树,那对下面这棵树我们又该怎么办,那就画呗,
先6,再左没有,再右,所有这是6和7,按照我们刚才的这种思路,分析的思路,是怎么办,采用递归的分析的思路,
你会发现对于每个左子树和右子树,他采用的处理是采用递归在处理的,把左子树看成是一棵完整的树,右子树看成
是一棵完整的树,再套用根,左右的规则就可以了,这是我们一个遍历的规则
2. 中序遍历:中序遍历该怎么办,先左,再根,再右,我把这个写完以后右边的大家一起来写,先是左子树,
然后是根,然后是右子树,左子树有好几条线,然后根是1,右子树有好多节点,先左,没有,再根,再右,
就可以了,我们这么来写,这是4,5,我们再看这一部分,先左只有一个,然后再根,再右,先来个几,3,
再来个2,对于这一块他还是颗树,这棵树我们应该怎么办,左没有,根6,然后呢7,怎么来的,我们写一下
先来个6,再来个7,这是一个内容,7怎么办,7就一个节点了,你怎么知道他是一个节点了,老师我一眼就看出来了
计算机是看不出来的,计算机怎么知道7是叶子节点了,左孩子和右孩子都是空
3. 后根遍历:大家想一下这个到底该是怎么回事呢,先左子树,再右子树,然后再是根,先是左,再是右,
最后是根,根是1,左边该怎么办,先左左边有吗,右边5,再是根4,这应该是5,4,右子树呢,同样是先左再右再根
我们得到结论是什么,3,2,中间这一块呢别着急,我们先画一个横线,递归的意思,然后怎么办,先左再右,
然后再根,7,6
二叉树的遍历就讲到这里了
已知一棵二叉树的后序遍历的序列为 5 4 3 7 6 2 1,中序遍历的序列为 4 5 1 3 2 6 7,
则其先序遍历的序列是什么?
我们是可以推出来的,怎么推呢,只要给出一个中序遍历,后序先序只要给你一个,你都可以把另外一种
给求出来,但是如果先给你一个先,再给你一个后,让你求中序,那不好说,那求不出来的,怎么可能,不理解,
我们拿过来,复制一下,中序后序,我又中序和后序,我怎么知道先序呢,那我当然知道了,后序最后一个肯定是根,
然后1是根了,一下子就可以确定谁是根了,然后1的左边就是左=子树,1的右边就是右子树,我们的树不用画,
直接就可以写出来
1. 先序遍历:DLR,D是Deque,根左右,一下子我们就知道谁是根了,1是根,左边是多少待定,右边是多少待定,
我们先看左边,左边只有两个数,一个4,一个5,什么叫后续遍历,最后一个肯定是根,先序遍历的话先写一个4,
后边就是左和右,左和右不管是谁直接写5就行了,到底是左还是右,可以看出来的,怎么看出来,4是根,是左是右
都有可能,5在后面的那就是右,所以刚才的图我们就可以画出来,先写一个1,然后再来画一下,这是4,5到底是左还是右
是可以画出来的,根的后面那肯定是右,这是5,下面就没有难度了,对于我们右边这4个节点同样这么来,告诉我怎么办,
后继遍历里面最后一个肯定就是根,这个就是根,前面就是左,后边就是右,这应该是2,左边是3,这个不用说了,
先是2,左边只有一个3,看我们写的对吗,14523到目前为止没有错,该这个6和7了,在后序里面后面的一个,那我们就知道了
一个是6,另外一个肯定是7,但是7是左还是右啊,画一下,这是我们的6,7在右边,这就是我们的7,
这是关于我们二叉树遍历的一些相关的算法,作序中序后序大家要会,层次代理我们就不说了,层次代理是怎么回事?
看着简单,我们一看就能把层次遍历写出来,代码不好写,我们先序中序后序看着写不好写,写代码非常简单,
可以用递归来实现,关于遍历的相关算法已经讲了
下面我们来看,我们写实现,二叉树每一个节点怎么表示,我们是不是已经写过二叉链表了,每个节点包括三部分,所以我们就这么写了
写二叉树的节点
package com.learn.btree;
/**
* 二叉链表的节点
* @author Leon.Sun
*
*/
public class Node {
/**
* 节点的值
*/
private Object value;
/**
* 左孩子
* 左子树的引用
* 同样为了处理方便,我们把private去掉
*/
// private Node leftChild;
Node leftChild;
/**
* 右子树的引用
*/
// private Node rightChild;
Node rightChild;
public Node() {
super();
}
public Node(Object value) {
super();
this.value = value;
}
public Node(Object value, Node leftChild, Node rightChild) {
super();
this.value = value;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
/**
* 这个toString该怎么写呢
* 输出三个属性的值就可以了
*/
@Override
public String toString() {
return "Node [value=" + value + ", leftChild=" + leftChild + ", rightChild=" + rightChild + "]";
}
}
package com.learn.btree;
/**
* 二叉树接口
* 可以有不同的实现类,每个类可以使用不同的存储结构,比如顺序结构、链式结构
* 链式结构可以是二叉的,也可以是三叉的
* @author Leon.Sun
*
*/
public interface BinaryTree {
/**
* 是否空树
* 你的树是空的吗,空是什么意思
* 有没有根节点,有根就不是空的了
* @return
*/
public boolean isEmpty();
/**
* 树结点数量
* 树里面有几个节点
* @return
*/
public int size();
/**
* 获取二叉树的高度
* 得到树的高度
* @return
*/
public int getHeight();
/**
* 查询指定值的结点
* 在树里面去找一个值
* 我去找20,要告诉我这里没有20才可以
* @param value
* @return
*/
public Node findKey(int value); // 查找
/**
* 前序递归遍历
* 这三个遍历要采用递归来实现
*/
public void preOrderTraverse();
/**
* 中序遍历递归操作
*/
public void inOrderTraverse();
/**
* 后序遍历递归操作
*/
public void postOrderTraverse();
/**
* 后序遍历递归操作
* 这个是重载,一个有参,一个无参
* @param node 树根结点
*/
public void postOrderTraverse(Node node);
/**
* 中序遍历非递归操作
* 1)对于任意节点current,若该节点不为空则将该节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,栈顶节点出栈,访问节点后将该节点的右子树置为current
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public void inOrderByStack();
/**
* 前序遍历非递归操作
* 1)对于任意节点current,若该节点不为空则访问该节点后再将节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,栈顶节点出栈,将该节点的右子树置为current
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public void preOrderByStack();
/**
* 后序遍历非递归操作
* 1)对于任意节点current,若该节点不为空则访问该节点后再将节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,取栈顶节点的右子树,如果右子树为空或右子树刚访问过,则访问该节点,并将preNode置为该节点
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public void postOrderByStack();
/**
* 按照层次遍历二叉树
* 这个需要借助队列来实现,这里还是包含很多技能点的
*/
public void levelOrderByStack();
}
package com.learn.btree;
/**
* 他要实现一下2我们的BinaryTree,要实现这个接口
* 你把这些方法掌握了,那二叉树的基本操作呢就基本上知道了
* 但不是所有的操作,基本操作都知道了
*
* @author Leon.Sun
*
*/
public class LinkedBinargyTree implements BinaryTree {
@Override
public boolean isEmpty() {
return false;
}
@Override
public int size() {
return 0;
}
@Override
public int getHeight() {
return 0;
}
@Override
public Node findKey(int value) {
return null;
}
@Override
public void preOrderTraverse() {
}
@Override
public void inOrderTraverse() {
}
@Override
public void postOrderTraverse() {
}
@Override
public void postOrderTraverse(Node node) {
}
@Override
public void inOrderByStack() {
}
@Override
public void preOrderByStack() {
}
@Override
public void postOrderByStack() {
}
@Override
public void levelOrderByStack() {
}
}
package com.learn.btree;
/**
* 在这个测试类里面我们要干什么,
* @author Leon.Sun
*
*/
public class Test {
public static void main(String[] args) {
/**
* 你想进行遍历,我想遍历一下这个数
* 我想看看这个数一共有几层,前提是创建一个二叉树
*
*/
/**
* 判断二叉树是否为空,这个简单
*/
/**
* 先序遍历递归
*/
/**
* 中序遍历递归
*/
/**
* 后续遍历递归
*/
/**
* 中序遍历非递归
* 中序遍历如果采用非递归的遍历
* 需要借助栈
*/
/**
* 按照层次遍历,借助队列
* 先把这些层次记住
*/
/**
* 在二叉树中查找某个值
*/
/**
* 二叉树的高度,他总共有几层
*/
/**
* 二叉树的节点数量
* 把我们要实现二叉树的框架,都给大家搭建出来了
*/
}
}