二叉树的遍历算法(三级)

知道二叉树在计算机里面是怎么存储的,有顺序存储结构,采用数组存储,也有链式存储结构,采用二叉链表,或者采用三叉链表,那采用

链式存储结构更多一点,下面我们会讲二叉树在计算机里的实现,就是链式存储结构的二叉链表为例来进行讲解的,讲这个具体实现之前呢,

我们要知道一下二叉树的遍历策略,



什么叫遍历?

大家想一下,如果是一个线性表的话,他的遍历是很简单的,怎么简单呢,比如说一个数组,来个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) {
		/**
		 * 你想进行遍历,我想遍历一下这个数
		 * 我想看看这个数一共有几层,前提是创建一个二叉树
		 * 
		 */
		
		/**
		 * 判断二叉树是否为空,这个简单
		 */
		
		/**
		 * 先序遍历递归
		 */
		
		/**
		 * 中序遍历递归
		 */
		
		/**
		 * 后续遍历递归
		 */
		
		/**
		 * 中序遍历非递归
		 * 中序遍历如果采用非递归的遍历
		 * 需要借助栈
		 */
		
		/**
		 * 按照层次遍历,借助队列
		 * 先把这些层次记住
		 */
		
		/**
		 * 在二叉树中查找某个值
		 */
		
		/**
		 * 二叉树的高度,他总共有几层
		 */
		
		/**
		 * 二叉树的节点数量
		 * 把我们要实现二叉树的框架,都给大家搭建出来了
		 */
		
		
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值