IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 二叉树的遍历 -> 正文阅读

[数据结构与算法]二叉树的遍历

?


?
?

??

二叉树的存储结构

顺序存储结构

??按照顺序存储结构的定义,在此约定,用一组地址连续的存储单元依次自上而下、自左至右存储完全二叉树上的结点元素,即将完全二叉树上编号为 i 的结点元素存储在定义的一维数组中下标为 i-1 的分量中。
顺序存储
?
?

链式存储结构

结构
??二叉树的结点(如上图),由一个数据元素和分别指向其左、右子树的两个分支构成,则表示二叉树的链表中的结点至少包含 3 个域:数据域和左、右指针域,如下图所示。二叉链表
三元素
??有时,为了便于找到结点的双亲,则还可以在结点结构中增加一个指向其双亲结点的指针域,如下图所示。三叉链表
四元素
??在含有 n 个结点的二叉链表中有 n+1 个空链域。
示例
二叉链表定义:

typedef struct BiTNode {
	TElemType data;
	struct BiTNode *lchild, *rchild;//左右孩子指针 
}BiTNode, *BiTree; 

?
?

遍历二叉树

遍历二叉树: 如何按某条搜索路径巡访树的每个结点,使得每个结点均被访问一次,而且仅被访问一次。“访问”的含义很广,可以是对结点作各种处理,如输出结点的信息等。
二叉树
?

二叉树建立

不管是先序、中序、后序建树,输入的序列都应为先序。

先序建树

void PreorderBiTree(BiTree &T){//先序建立二叉树 

	char a;
	scanf("%c",&a);
	if(a == '#'){
		T = NULL;
	}	
	else{
		T = (BiTree)malloc(sizeof(BiTNode));
		if(!T){
			printf("内存分配出现问题!!!\n");
			printf("请重新启动程序!!!");
			exit(0);
		}
		T->data = a;//将数据赋值给该节点
		PreorderBiTree(T->lchild);//建立左孩子 
		//printf("%c ",T->data); 
		PreorderBiTree(T->rchild);//建立右孩子	
	}
}

中序建树

void InOrderBiTree(BiTree &T){//中序建立二叉树 

	char a;
	scanf("%c",&a);
	if(a == '#'){
		T = NULL;
	}	
	else{
		T = (BiTree)malloc(sizeof(BiTNode));
		if(!T){
			printf("内存分配出现问题!!!\n");
			printf("请重新启动程序!!!");
			exit(0);
		}
		InOrderBiTree(T->lchild);//建立左孩子 
		T->data = a;//将数据赋值给该节点
		//printf("%c ",T->data); 
		InOrderBiTree(T->rchild);//建立右孩子	
	}
}

后序建树

void PostorderBiTree(BiTree &T){//后序建立二叉树 

	char a;
	scanf("%c",&a);
	if(a == '#'){
		T = NULL;
	}	
	else{
		T = (BiTree)malloc(sizeof(BiTNode));
		if(!T){
			printf("内存分配出现问题!!!\n");
			printf("请重新启动程序!!!");
			exit(0);
		}
		PostorderBiTree(T->lchild);//建立左孩子 
		PostorderBiTree(T->rchild);//建立右孩子
		T->data = a;//将数据赋值给该节点	
	}
}

?

递归算法

先序遍历(中左右)

先序遍历二叉树的操作定义:
若二叉树为空,则空操作;否则:
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树。
遍历顺序:-+a*b-cd/ef(波兰式)

void PreorderTraverse(BiTree T){//先序遍历输出

	if(T){
		PrintElement(T->data);//输出data 
		PreorderTraverse(T->lchild);
		PreorderTraverse(T->rchild);
	} 
	return;
}

?

中序遍历(左中右)

中序遍历二叉树的操作定义:
若二叉树为空,则空操作;否则:
(1)先序遍历左子树;
(2)访问根结点;
(3)先序遍历右子树。
遍历顺序:a+b*c-d-e/f(逆波兰式)

void InOrderTraverse(BiTree T){//中序遍历输出 
	
	if(T){
		
		InOrderTraverse(T->lchild);
		PrintElement(T->data);//输出data 
		InOrderTraverse(T->rchild);
	}
	return;
}

?

后序遍历(左右中)

后序遍历二叉树的操作定义:
若二叉树为空,则空操作;否则:
(1)先序遍历左子树;
(2)先序遍历右子树;
(3)访问根结点。
遍历顺序:abcd-*+ef/-(逆波兰式)

void PostorderTraverse(BiTree T){//后序遍历输出 
	
	if(T){
		PostorderTraverse(T->lchild);
		PostorderTraverse(T->rchild);
		PrintElement(T->data);//输出data 
	} 
	return;
	
}

?

非递归算法

先序遍历(中左右)

(1)访问结点P,并将结点P入栈;
(2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至(1);若不为空,则将P的左孩子置为当前的结点P;
(3)直到P为NULL并且栈为空,则遍历结束。

void PreorderTraverse(BiTree T){//先序遍历(非递归)
	 
	BiTree S[1000];//栈 
	int top = 0;//栈顶标记 
	BiTree e = T;
	if(!e){
		return;
	}
	while(e || top > 0){
		while(e){
			printf("%c",e->data);//访问结点 
			S[top++] = e;//入栈 
			e = e->lchild; 
		}
		e = S[top-1];//返回栈顶 
		top--; //出栈
		e = e->rchild;	
	}	
}

?

中序遍历(左中右)

(1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
(2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
(3)直到P为NULL并且栈为空则遍历结束。

void InOrderTraverse(BiTree T){//中序遍历(非递归) 
	
	BiTree S[1000];//栈 
	int top = 0;//栈顶标记 
	BiTree e = T;
	if(!e){
		return;
	}
	
	while(e || top > 0){
		while(e){
			S[top++] = e;
			e = e->lchild;
		}
		
		e = S[top-1];
		top--;
		printf("%c",e->data);//访问结点 
		e = e->rchild;
	}
}

?

后序遍历(左右中)

对于一个节点而言,要实现访问顺序为左儿子-右儿子-根节点,
可以利用后进先出的栈,在节点不为空的前提下,依次将根节点,右儿子,左儿子压栈。
故我们需要按照根节点-右儿子-左儿子的顺序遍历树,
而我们已经知道先序遍历的顺序是根节点-左儿子-右儿子,
故只需将先序遍历的左右调换并把访问方式打印改为压入另一个栈即可。
最后一起打印栈中的元素。

void PostorderTraverse1(BiTree T){//后序遍历(非递归) 
	
	BiTree S[1000];//栈 
	BiTree H[1000];  
	int tops = 0,toph = 0;
	BiTree e = T;//f为标记 
	
	if(!e){
		return;
	}
	
	while(e || tops > 0){
		while(e){
			S[tops++] = e;
			H[toph++] = e;
			e = e->rchild;
		}
		
		e = S[tops - 1];
		tops--;
		e = e->lchild;
		
	}
	while(toph > 0){
		printf("%c",H[toph - 1]->data);
		toph--;
	}
}

要访问一个节点的条件上一个访问的节点是右儿子。我们可以增加一个变量Prev来判断当前节点Curr的上一个节点与它的关系来执行相应的操作。
    若Prev为空(Curr节点是根节点)或者Prev是Curr的父节点,将Curr节点的左孩子和右孩子分别压入栈;
    若Prev是Curr的左儿子,则将Curr的右儿子压入栈;
    否则Prev是Curr的右儿子,访问Curr;

void PostorderTraverse2(BiTree T){//后序遍历(非递归)

	BiTree S[1000];//栈
	int top = 0;
	S[top++] = T;
	BiTree e = NULL,f = NULL;
	
	while(top > 0){
		e = S[top-1];
		if(f == NULL || f->lchild == e || f->rchild == e){
			if(e->lchild)
				S[top++] = e->lchild;
			else if(e->rchild)
				S[top++] = e->rchild;
		}
		else if(e->lchild == f){
			if(e->rchild)
				S[top++] = e->rchild;
		}
		else{
			printf("%c",e->data);
			top--;
		}
		f = e;
		
	} 
	
}

?
?

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:38:39  更:2021-08-17 15:40:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:28:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码