二叉树各种遍历

树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用。

二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用作二叉查找树和二叉堆或是二叉排序树。二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2的 i -1次方个结点;深度为k的二叉树至多有2^(k) -1个结点;对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,度为2的结点数为n2,则n0 = n2 + 1。

二叉树的链式存储结构是一类重要的数据结构,其形式定义如下:

//二叉树结点
typedef struct BiTNode{
	//数据
	char data;
	//左右孩子指针
	struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

二叉树的创建:

通过读入一个字符串,建立二叉树的算法如下:

//按先序序列创建二叉树
int CreateBiTree(BiTree &T){
	char data;
	//按先序次序输入二叉树中结点的值(一个字符),‘#’表示空树
	scanf("%c",&data);
	if(data == ‘#‘){
		T = NULL;
	}
	else{
		T = (BiTree)malloc(sizeof(BiTNode));
		//生成根结点
		T->data = data;
		//构造左子树
		CreateBiTree(T->lchild);
		//构造右子树
		CreateBiTree(T->rchild);
	}
	return 0;
}

二叉树的遍历:

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。

递归算法:

//输出
void Visit(BiTree T){
	if(T->data != ‘#‘){
		printf("%c ",T->data);
	}
}
//先序遍历
void PreOrder(BiTree T){
	if(T != NULL){
		//访问根节点
		Visit(T);
		//访问左子结点
		PreOrder(T->lchild);
		//访问右子结点
		PreOrder(T->rchild);
	}
}
//中序遍历
void InOrder(BiTree T){
	if(T != NULL){
		//访问左子结点
		InOrder(T->lchild);
		//访问根节点
		Visit(T);
		//访问右子结点
		InOrder(T->rchild);
	}
}
//后序遍历
void PostOrder(BiTree T){
	if(T != NULL){
		//访问左子结点
		PostOrder(T->lchild);
		//访问右子结点
		PostOrder(T->rchild);
		//访问根节点
		Visit(T);
	}
}

非递归算法:

<1>先序遍历:

【思路】:访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。

/* 先序遍历(非递归)
   思路:访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。
*/
void PreOrder2(BiTree T){
	stack<BiTree> stack;
	//p是遍历指针
	BiTree p = T;
	//栈不空或者p不空时循环
	while(p || !stack.empty()){
		if(p != NULL){
			//存入栈中
			stack.push(p);
			//访问根节点
			printf("%c ",p->data);
			//遍历左子树
			p = p->lchild;
		}
		else{
			//退栈
			p = stack.top();
			stack.pop();
			//访问右子树
			p = p->rchild;
		}
	}//while
}

<2>中序遍历

【思路】:T是要遍历树的根指针,中序遍历要求在遍历完左子树后,访问根,再遍历右子树。
         先将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,访问T->data,再中序遍历T的右子树。

void InOrder2(BiTree T){
	stack<BiTree> stack;
	//p是遍历指针
	BiTree p = T;
	//栈不空或者p不空时循环
	while(p || !stack.empty()){
		if(p != NULL){
			//存入栈中
			stack.push(p);
			//遍历左子树
			p = p->lchild;
		}
		else{
			//退栈,访问根节点
			p = stack.top();
			printf("%c ",p->data);
			stack.pop();
			//访问右子树
			p = p->rchild;
		}
	}//while
}

<3>后序遍历

【思路】:T是要遍历树的根指针,后序遍历要求在遍历完左右子树后,再访问根。需要判断根结点的左右子树是否均遍历过。

//后序遍历(非递归)
typedef struct BiTNodePost{
	BiTree biTree;
	char tag;
}BiTNodePost,*BiTreePost;

void PostOrder2(BiTree T){
	stack<BiTreePost> stack;
	//p是遍历指针
	BiTree p = T;
	BiTreePost BT;
	//栈不空或者p不空时循环
	while(p != NULL || !stack.empty()){
		//遍历左子树
		while(p != NULL){
			BT = (BiTreePost)malloc(sizeof(BiTNodePost));
			BT->biTree = p;
			//访问过左子树
			BT->tag = ‘L‘;
			stack.push(BT);
			p = p->lchild;
		}
		//左右子树访问完毕访问根节点
		while(!stack.empty() && (stack.top())->tag == ‘R‘){
			BT = stack.top();
			//退栈
			stack.pop();
			BT->biTree;
			printf("%c ",BT->biTree->data);
		}
		//遍历右子树
		if(!stack.empty()){
			BT = stack.top();
			//访问过右子树
			BT->tag = ‘R‘;
			p = BT->biTree;
			p = p->rchild;
		}
	}//while
}

<4>层次遍历

【思路】:按从顶向下,从左至右的顺序来逐层访问每个节点,层次遍历的过程中需要用队列。

//层次遍历
void LevelOrder(BiTree T){
	BiTree p = T;
	//队列
	queue<BiTree> queue;
	//根节点入队
	queue.push(p);
	//队列不空循环
	while(!queue.empty()){
		//对头元素出队
		p = queue.front();
		//访问p指向的结点
		printf("%c ",p->data);
		//退出队列
		queue.pop();
		//左子树不空,将左子树入队
		if(p->lchild != NULL){
			queue.push(p->lchild);
		}
		//右子树不空,将右子树入队
		if(p->rchild != NULL){
			queue.push(p->rchild);
		}
	}
}

测试用例:

输入:

ABC##DE#G##F###

输出:

代码:

#include<iostream>
#include<stack>
#include<queue>
using namespace std;

//二叉树结点
typedef struct BiTNode{
	//数据
	char data;
	//左右孩子指针
	struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//按先序序列创建二叉树
int CreateBiTree(BiTree &T){
	char data;
	//按先序次序输入二叉树中结点的值(一个字符),‘#’表示空树
	scanf("%c",&data);
	if(data == ‘#‘){
		T = NULL;
	}
	else{
		T = (BiTree)malloc(sizeof(BiTNode));
		//生成根结点
		T->data = data;
		//构造左子树
		CreateBiTree(T->lchild);
		//构造右子树
		CreateBiTree(T->rchild);
	}
	return 0;
}
//输出
void Visit(BiTree T){
	if(T->data != ‘#‘){
		printf("%c ",T->data);
	}
}
//先序遍历
void PreOrder(BiTree T){
	if(T != NULL){
		//访问根节点
		Visit(T);
		//访问左子结点
		PreOrder(T->lchild);
		//访问右子结点
		PreOrder(T->rchild);
	}
}
//中序遍历
void InOrder(BiTree T){
    if(T != NULL){
        //访问左子结点
        InOrder(T->lchild);
        //访问根节点
        Visit(T);
        //访问右子结点
        InOrder(T->rchild);
    }
}
//后序遍历
void PostOrder(BiTree T){
	if(T != NULL){
		//访问左子结点
		PostOrder(T->lchild);
		//访问右子结点
		PostOrder(T->rchild);
		//访问根节点
		Visit(T);
	}
}
/* 先序遍历(非递归)
   思路:访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。
*/
void PreOrder2(BiTree T){
	stack<BiTree> stack;
	//p是遍历指针
	BiTree p = T;
	//栈不空或者p不空时循环
	while(p || !stack.empty()){
		if(p != NULL){
			//存入栈中
			stack.push(p);
			//访问根节点
			printf("%c ",p->data);
			//遍历左子树
			p = p->lchild;
		}
		else{
			//退栈
			p = stack.top();
			stack.pop();
			//访问右子树
			p = p->rchild;
		}
	}//while
}
/* 中序遍历(非递归)
   思路:T是要遍历树的根指针,中序遍历要求在遍历完左子树后,访问根,再遍历右子树。
         先将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,访问T->data,再中序遍历T的右子树。
*/
void InOrder2(BiTree T){
	stack<BiTree> stack;
	//p是遍历指针
	BiTree p = T;
	//栈不空或者p不空时循环
	while(p || !stack.empty()){
		if(p != NULL){
			//存入栈中
			stack.push(p);
			//遍历左子树
			p = p->lchild;
		}
		else{
			//退栈,访问根节点
			p = stack.top();
			printf("%c ",p->data);
			stack.pop();
			//访问右子树
			p = p->rchild;
		}
	}//while
}

//后序遍历(非递归)
typedef struct BiTNodePost{
	BiTree biTree;
	char tag;
}BiTNodePost,*BiTreePost;

void PostOrder2(BiTree T){
	stack<BiTreePost> stack;
	//p是遍历指针
	BiTree p = T;
	BiTreePost BT;
	//栈不空或者p不空时循环
	while(p != NULL || !stack.empty()){
		//遍历左子树
		while(p != NULL){
			BT = (BiTreePost)malloc(sizeof(BiTNodePost));
			BT->biTree = p;
			//访问过左子树
			BT->tag = ‘L‘;
			stack.push(BT);
			p = p->lchild;
		}
		//左右子树访问完毕访问根节点
		while(!stack.empty() && (stack.top())->tag == ‘R‘){
			BT = stack.top();
			//退栈
			stack.pop();
			printf("%c ",BT->biTree->data);
		}
		//遍历右子树
		if(!stack.empty()){
			BT = stack.top();
			//访问过右子树
			BT->tag = ‘R‘;
			p = BT->biTree;
			p = p->rchild;
		}
	}//while
}
//层次遍历
void LevelOrder(BiTree T){
	BiTree p = T;
	//队列
	queue<BiTree> queue;
	//根节点入队
	queue.push(p);
	//队列不空循环
	while(!queue.empty()){
		//对头元素出队
		p = queue.front();
		//访问p指向的结点
		printf("%c ",p->data);
		//退出队列
		queue.pop();
		//左子树不空,将左子树入队
		if(p->lchild != NULL){
			queue.push(p->lchild);
		}
		//右子树不空,将右子树入队
		if(p->rchild != NULL){
			queue.push(p->rchild);
		}
	}
}
int main()
{
	BiTree T;
	CreateBiTree(T);
	printf("先序遍历:\n");
	PreOrder(T);
	printf("\n");
	printf("先序遍历(非递归):\n");
	PreOrder2(T);
	printf("\n");
	printf("中序遍历:\n");
	InOrder(T);
	printf("\n");
	printf("中序遍历(非递归):\n");
	InOrder2(T);
	printf("\n");
	printf("后序遍历:\n");
	PostOrder(T);
	printf("\n");
	printf("后序遍历(非递归):\n");
	PostOrder2(T);
	printf("\n");
	printf("层次遍历:\n");
	LevelOrder(T);
	printf("\n");
    return 0;
}
时间: 2024-10-06 23:51:36

二叉树各种遍历的相关文章

【树4】二叉树的遍历

简介 遍历二叉树就是按照某种顺序,将树中的结点都枚举一遍,且每个结点仅仅访问一次.因为树不是线性的结构,遍历不像线性表那样简单,因此他的遍历需要特点的算法来完成. 从某种角度讲,对二叉树的遍历就是将树形结构转换为线性结构的操作. 二叉树的遍历方法主要有如下几种: 先序遍历:先访问root结点,再先序遍历左子树,再先序遍历右子树. 中序遍历:先中序遍历左子树,再访问root结点,再中序遍历右子树. 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问root结点. 层遍历:从上到下,从左到右,一层

树、二叉树、遍历二叉树的总结

首先介绍树: 如上图所示就是一棵树,先介绍树的几个关键名词: 节点:A.B.C.D等都叫节点 节点的度:节点有几个分支,就叫节点的度,比如节点B有2个分支,那B的度为2 终端节点(叶子):没有分支的节点,如E.F.G.H 非终端节点:有分支的节点,如A.B.D.C 节点的层次:自上而下排列层次,A为1层,B为2层,D为3层 树的度:哪个节点的度最大,这个最大的度就是树的度,如图树的度为2 树的深度:简而言之,就是树有几层,如图的树的深度为4 我们接触最多的树是二叉树 二叉树:在计算机科学中,二叉

java生成二叉树和遍历

在java中实现二叉树和链表的方法都是在类中定义该类的对象引用 比如 class Tree { int data; Tree left; Tree right; } 这样的话当我们new一个Tree对象的时候,该对象就拥有了left和right两个对象,这样就起到了连接的 作用,在链表中就是连接了下一个,在树中就相当于边,这样就起到一个接一个的效果.总之,就是吧对象连接起来了. 下面是完整代码 package code; public class TwoTree { public static

【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】

[107-Binary Tree Level Order Traversal II(二叉树层序遍历II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). For example

4-9 二叉树的遍历 (25分)

4-9 二叉树的遍历   (25分) 输出样例(对于图中给出的树): Inorder: D B E F A G H C I Preorder: A B D F E C G H I Postorder: D E F B H G I C A Levelorder: A B C D F G I E H 代码:(都是遍历的算法) 1 // 4-9 二叉树的遍历 2 // 3 // Created by Haoyu Guo on 04/02/2017. 4 // Copyright ? 2017 Haoy

Java数据结构-二叉树及其遍历

二叉树的定义:n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互相不相交的.分别称为根结点的左子树和右子树的二叉树组成. 二叉树的特点: 0<=度<=2: 左右子树是有顺序的,不能颠倒: 不论有几棵子树,也要区分它是左子树还是右子树. 二叉树的五种基本形态: 空二叉树: 只有一个根结点: 根结点只有左子树: 根结点只有右子树: 根结点既有左子树又有右子树. 举例3个结点的二叉树的形态有: 下面说一些特殊的二叉树. 斜树:所有的结点都只有左子树的二叉

【转】算法之二叉树各种遍历

http://blog.csdn.net/sjf0115/article/details/8645991 树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:

编程之美问题之二叉树层序遍历多种解法

二叉树的层序遍历(要求区分层,例如每层遍历完输出换行) 单单层序遍历非常简单,一个队列就搞定了,但是区分层则要麻烦些.总的思路无非就是在每次print的时候,要能通过某个东西 区分出当前节点是否是一层最后一个节点,或者下一层的最后一个节点,感觉有点类似于机器学习中找个区分度明显的特征: 1.自己的解法,在单队列基础上,输入队列的数据添加一个标志 ,LevelHeaded,同时,在后面插入两个孩子的时候,判断是否这次输出的是队头,如果是的话,先插 个队头标志,再插入孩子,而且插入一次之后,后面不能

数据结构第三部分:树与树的表示、二叉树及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树、集合及其运算

参考:浙大数据结构(陈越.何钦铭)课件 1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R 中找出关键字与K 相同的记录).一个自然的问题就是,如何实现有效率的查找? 静态查找:集合中记录是固定的,没有插入和删除操作,只有查找 动态查找:集合中记录是动态变化的,除查找,还可能发生插入和删除 静态查找——方法一:顺序查找(时间复杂度O(n)) int

数据结构——二叉树的遍历

"树"是一种重要的数据结构,本文浅谈二叉树的遍历问题,採用C语言描写叙述. 一.二叉树基础 1)定义:有且仅有一个根结点,除根节点外,每一个结点仅仅有一个父结点,最多含有两个子节点,子节点有左右之分. 2)存储结构 二叉树的存储结构能够採用顺序存储,也能够採用链式存储,当中链式存储更加灵活. 在链式存储结构中,与线性链表类似,二叉树的每一个结点採用结构体表示,结构体包括三个域:数据域.左指针.右指针. 二叉树在C语言中的定义例如以下: struct BiTreeNode{ int c;