二叉树、二叉树的建立及其前、中、层序遍历

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43296323/article/details/102573503

一、什么是二叉树

二叉树是数据结构中较为重要的一部分内容。
二叉树常被用于实现二叉查找树和二叉堆。

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”。

根据不同的用途可分为:

1、完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。

2、满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

深度为h的二叉树最多有个结点(h>=1),最少有h个结点。对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1。

有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系为若I为结点编号则 如果I>1,则其父结点的编号为I/2。如果2I<=N,则其左孩子(即左子树的根结点)的编号为2I。若2I>N,则无左孩子。
如果2
I+1<=N,则其右孩子的结点编号为2*I+1。

二、如何建立二叉树

这里我们采用队列、前序的方式来建立二叉树,结合两者特性
(1)队列:先进先出,依次入队。
(2)前序:根、左、右顺序。

综上,我们可以形成这样的思路,假设我们从一组字符串中获取数据存入二叉树中,例如{1,2,3,4,5,6,7,}。那么这里我们就先让1进入队列然后出队,再将2,3依次加入队列,这就是根节点和它最初的左右子树,之后的2,3又会分别作为根节点,通过循环继续加入他们的左右子树,知道队列为空,即输入的数据都被存入了二叉树结构中。下面我们来看代码

//树节点 
public class Treenode {
	public int data;
	public Treenode left;
	public Treenode right;
	public Treenode(int data) {
		this.data = data;
	}
}
//建树过程
//假设下列建树过程给定的字符串是{1,2,3,4,5,6,7,}
public static Treenode createTreeByString(String order){
		order = order.trim();//去除输入字符串中误输入的空格
		if(order.length() == 0){
			return null;
		}
		String[] parts = order.split(",");
		//将给定的用“,”分隔的字符串中的“,”去除
		Queue<Treenode> queue = new LinkedList<Treenode>();//建立队列
		int index = 0;
		String item = parts[0];
		Treenode root = new Treenode(Integer.parseInt(item));
		queue.offer(root);//根节点进入队列中
		
		while(!queue.isEmpty()){
			if(index + 1 >= parts.length) break;
			Treenode polled = queue.poll();//节点出队
			index = index + 1;
			item = parts[index];
			item = item.trim();
			if(!item.equals("null")){//考虑根节点左子树
				Treenode left = new Treenode(Integer.parseInt(item));
				polled.left = left;
				queue.offer(left);
			}
			if(index + 1 >= parts.length) break;
			
			index = index + 1;
			item = parts[index];
			item = item.trim();
			if(!item.equals("null")){//考虑根节点右子树
				Treenode right = new Treenode(Integer.parseInt(item));
				polled.right = right;
				queue.offer(right);
			}
		}
		return root;
	}

三、二叉树的遍历

(1)前序遍历

二叉树的前序遍历,是以根左右的方式进行遍历,我们以下图这棵树为例子
在这里插入图片描述
虽然这棵树画的很丑,但是请大家随意吐槽。
那么这棵树遍历后的结果应该就是{1,2,4,5,3,6,7}
下面我们分迭代、递归两种方式来给大家实现

递归

先获取根节点然后左子树,然后右子树,但是要注意递归结束的条件,即当前根节点为空的时候停止。

public static void pre1orderRecursive(Treenode root,List<Integer> ans) {
		if(root ==null) {//前序递归
			return;
		}
		ans.add(root.data);
		pre1orderRecursive(root.left, ans);
		pre1orderRecursive(root.right, ans);
	}
	public static void main(String[] args){
		
		Treenode root = createTreeByString(" 1,2,3,4,5,6,7");
		List<Integer> ans = new ArrayList<Integer>();
		pre1orderRecursive(root,ans);//前序递归
		System.out.println(ans);
	}
迭代

我们在用迭代的时候,需要用到栈区,这就不得不提到栈区的特点,后进先出,。所以我们在将根节点的左右子树压入栈区的时候要先将右子树压入,后将左子树压入。然后左子树出栈右子树出栈,知道整个栈区为空这样才能够保证获得的数据是以根左右的形式遍历出来的。

public static void preorderRecursive(Treenode root, List<Integer> ans){
		Stack<Treenode> stack = new Stack<Treenode>();//前序迭代
		if(root==null)
		{
			return ;
		}
		stack.push(root);
		
		while(!stack.isEmpty()){
			root = stack.pop();
			ans.add(root.data);
			if(root.right!=null){
				stack.push(root.right);
				
			}
			if(root.left!=null){
				stack.push(root.left);
			}
		}
		
	}
	public static void main(String[] args){
		
		Treenode root = createTreeByString(" 1,2,3,4,5,6,7");
		List<Integer> ans = new ArrayList<Integer>();
		preorderRecursive(root,ans);//前序迭代
		System.out.println(ans);
	}

(2)中序遍历

中序遍历的方式是左根右

递归

我个人认为中序和前序的递归方法基本相似,只不过是把代码的顺序换了一下而已,很容易就想通了

public static void mid1orderRecursive(Treenode root, List<Integer> ans){
	if(root == null){//中序递归
		return;
	}
	mid1orderRecursive(root.left, ans);
	ans.add(root.data);
	mid1orderRecursive(root.right, ans);
	
}
public static void main(String[] args){
		
		Treenode root = createTreeByString(" 1,2,3,4,5,6,7");
		List<Integer> ans = new ArrayList<Integer>();
		mid1orderRecursive(root,ans);//中序递归
		System.out.println(ans);
	}

迭代

由于中序是按照左根右的方式进行的,所以我们需要先考虑左子树整体,暂时将右子树放在一边。我们由下图进行讲解
在这里插入图片描述
我们在处理这颗树的时候,首先根节点1入栈,因为1拥有左子树,所以继续向下走,到2,让2入栈,2也有左子树,所以我们再继续。直到4,让2入栈,因为4没有子树,即4是叶子结点,这个时候我们的栈区中已经有了由上到下三个数据4、2、1,此时我们从4开始出栈,然后2出栈,但是这里要注意的是,2是有右子树的所以这里2出栈后还要让其右子树5入栈,出栈。然后是根节点1出栈,进行到这里,我们已经完成了整个二叉树的左子树以及根节点的遍历过程,此时我们再去考虑右子树但是这时候我们会面临一个问题,此时我们的根节点1出栈之后,我们的栈区已经没有数据了,如果我们要是还像上面说到的那样通过判断栈区是否为空来确定循环的截止的话,就会出现,只遍历了一棵不完整的树的情况,即只获取了所有的可作为根节点的数据及其左子树的数据,所以我们还要修改判断的方式,来使得程序可以继续执行右子树,我这里采用的办法是通过把stack.peek()函数,将根节点设置为当前出栈的节点的右子树,然后增加判断条件,使得程序可以继续执行。下面是代码

public static void midorderRecursive(Treenode root,List<Integer> ans){
		//中序迭代
		Stack<Treenode> stack = new Stack<Treenode>();
		if(root==null){
			return ;
		}
		while(!stack.isEmpty() || root!=null){
			
			while(root!=null){
				stack.push(root);
				root = root.left;
			}
			if(!stack.isEmpty()){
				ans.add(stack.peek().data);
				root = stack.peek().right;
				stack.pop();
			}
		}
		
	}
	public static void main(String[] args){
		
		Treenode root = createTreeByString(" 1,2,3,4,5,6,7");
		List<Integer> ans = new ArrayList<Integer>();
		midorderRecursive(root,ans);//中序迭代
		System.out.println(ans);
	}

(3)后序遍历

由于后序遍历难度较大,且实用性较差一些,所以这里就不做讲述

(4)层序遍历

层序遍历就是将二叉树由上到下一层层输出出来

递归DFS

我们递归的方式要考虑一点,之前在做前序和中序的时候,我们都是找到一个数据就直接输出出来,或者存到数组里,最后输出出来。但是层序输出是要求输出顺序的,然而递归的方法可以说就是一条路走到黑的方式,我们如果还是像之前这样做,肯定的不到层序输出的结果,所以我们可以投机取巧,将数据存到数组里,当然,这个数组不能是普通的数组,要用二维数组。然后从第一行第一个位置开始一直到最后一个位置,输出相应的数据。

public static void Level1orderRecursive(Treenode root,int level,List<List<Integer>> ans){
		if(root == null){//层序递归DFS
			return;
		}
		if(level+1 >= ans.size()) {
			ans.add(new LinkedList<Integer>());
		}
		ans.get(level).add(root.data);
		Level1orderRecursive(root.left,level+1,ans);
		Level1orderRecursive(root.right,level+1,ans);
		}
		public static void main(String[] args){
		
		Treenode root = createTreeByString(" 1,2,3,4,5,6,7");
		List<List<Integer>> ary = new LinkedList<List<Integer>>();
		Level1orderRecursive(root,0,ary);//层序递归DFS
		
		System.out.println(ary);
	}

迭代BFS

迭代过程主要用队列实现,根节点入队,根节点出队,判断当前跟节点有没有左右子树。然后分别入队,然后由此往复执行,知道最终队列为空为止。

public static void LevelorderRecursive(Treenode root, List<Integer> ans){
		if(root == null){//层序迭代BFS
			return;
		}
		Queue<Treenode> queue = new LinkedList<>();
		queue.add(root);
		while(!queue.isEmpty()) {
			Treenode t = queue.poll();
			ans.add(t.data);
			if(t.left!=null) {
				queue.add(t.left);
			}
			if(t.right!=null) {
				queue.add(t.right);
			}
		}
	}
	public static void main(String[] args){
		
		Treenode root = createTreeByString(" 1,2,3,4,5,6,7");
		List<List<Integer>> ary = new LinkedList<List<Integer>>();
		Level1orderRecursive(root,0,ary);//层序递归DFS
		
		System.out.println(ary);
	}

这些就是我最近对树的相关学习,欢迎各位大佬对我的不足之处提出建议,也欢迎大家吐槽!

猜你喜欢

转载自blog.csdn.net/weixin_43296323/article/details/102573503