树与二叉树超级详细知识点(三)----二叉树的存储结构及遍历(未完结)

图1

存储结构

顺序存储

依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适;但对于一般的二叉树,为让数组和下标与二叉树中结点之间的逻辑关系,只能添加空结点,使用顺序存储会造成大量的空间浪费。

图2

/* 顺序存储代码 */
struct TreeNode{
	ElemType value; // 结点中数据元素
	bool isEmpty;  //结点是否为空
}

链式存储

顺序存储空间利用率低,因此一般采用链式存储.
二叉树的链式存储称为二叉链表,二叉链表至少包含3个域:数据域data左指针域lchild右指针域rchild.
图3

/* 链式存储代码 */
typedef struct BiTNode{
	ElemTyle data;  // 数据域
	struct BiTNode *lchild, *rchild;  // 左指针域、右指针域
}BiTNode, *BiTree;

补:n个结点的二叉链表共有n+1个空链域

二叉树的遍历

遍历的基本知识

根据根结点、左子树结点、右子树结点的遍历顺序(重要),可分为:

  • 先序遍历: 根左右(NLR)
  • 中序遍历: 左根右(LNR)
  • 后序遍历: 左右根(LRN)

二叉树的遍历没有明确说明,一般是针对二叉链表的遍历

递归遍历

在这里插入图片描述
二叉树递归遍历特性:

  1. 要么是空树
  2. 要么是二叉树

遍历时要考虑空树的情况

先序递归遍历(PreOrder)

先序递归遍历的思路:

  1. 若二叉树为空,则什么也不做
  2. 若二叉树非空,先访问根结点,其次是遍历左子树,最后是遍历右子树
    在这里插入图片描述
/* 二叉链表先序递归遍历代码 */
void PreOrder(BiTree T){
	if (T != NULL){  			// 判空操作
		visit(T);  				// 访问根结点
		PreOrder(T->lchild);  	// 遍历左子树
		PreOrder(T->rchild);  	// 遍历右子树
	}
} 
中序递归遍历(InOrder)

中序递归遍历的思路:

  1. 若二叉树为空,则什么也不做
  2. 若二叉树非空,先遍历左子树,其次是访问根结点,最后是遍历右子树
    在这里插入图片描述
/* 二叉链表中序递归遍历代码 */
void InOrder(BiTree T){
	if (T != NULL){  			// 判空操作
		InOrder(T->lchild);  	// 遍历左子树
		visit(T);				// 访问根结点
		InOrder(T->rchild); 	// 遍历右子树
	}
}
后序递归遍历(PostOrder)

后序递归遍历的思路:

  1. 若二叉树为空,则什么也不做
  2. 若二叉树非空,先遍历左子树,其次是遍历右子树,最后是访问根结点
    在这里插入图片描述
/* 二叉链表后序递归遍历代码 */
void PostOrder(BiTree T){
	if (T != NULL){  			// 判空操作
		PostOrder(T->lchild);  	// 遍历左子树						
		PostOrder(T->rchild); 	// 遍历右子树
		visit(T);				// 访问根结点
	}
}
三种递归遍历方法总结
  • 以上三种遍历方法模式基本一样,无非是访问的顺序不同
  • 每个结点有且仅被访问一次
  • 时间复杂度为O(n)

非递归遍历

除了递归遍历外,还可以采用非递归的方法遍历二叉链表。通常通过栈来实现非递归的二叉树遍历

先序非递归遍历

先序非递归遍历思路:

  1. 沿着根的左孩子,依次访问左孩子并入栈,直到左孩子为空
  2. 栈顶元素出栈:若其右孩子空,则继续执行2;若其右孩子不空,将右子树转执行1
/* 二叉链表先序非递归遍历代码 */
void PreOrder2(BiTree T){
	InitStack(S);					// 初始化栈S
	BiTree p = T;					// p是遍历指针
	while( p || !IsEmpty(S)){		// 栈不空或p不空时循环
		if (p){						// 判空操作 // p指针为非空,一路向左走
			visit(p);				// 访问当前结点
			Push(S, p);				// 当前结点入栈
			p = p -> lchild;		// p指针赋值当前结点的左孩子
		} else {					// p指针为空,向右子树走
			Pop(S, p);				// 栈顶元素出栈
			p = p -> rchild;		// p指针赋值当前结点的右孩子
		}
	}
}
中序非递归遍历

中序非递归遍历思路:

  1. 沿着根的左孩子,依次入栈,直到左孩子为空
  2. 栈顶元素出栈并访问:若其右孩子空,则继续执行2;若其右孩子不空,将右子树转执行1
/* 二叉链表中序非递归遍历代码 */
void InOrder2(BiTree T){
	InitStack(S);				// 初始化栈S
	BiTree p = T;				//p是遍历指针
	while( p || !IsEmpty(S)){	// 栈不空或p不空时循环
		if (p){					// 判空操作 // p指针为非空,一路向左走
			Push(S, p);			// 当前结点入栈
			p = p -> lchild;	// p指针赋值当前结点的左孩子
		} else {				// p指针为空,向右子树走
			Pop(S, p);			// 栈顶元素出栈
			visit(p);			// 访问当前结点
			p = p -> rchild;	// p指针赋值当前结点的右孩子
		}
	}
}
后序非递归遍历

后序非递归遍历实现是三种非递归遍历中最难的。因为在后序遍历中,要保证左孩子和有孩子的已被访问且左孩子在右孩子前访问,才能访问根结点,这就给流程控制带来了难题。

非递归后序遍历的思路:

  1. 沿着根的左子树,依次入栈,直到左孩子为空
  2. 读取栈顶元素;若其右孩子不空且未被访问过,将右子树转执行1;否则,栈顶元素出栈并访问

在上述思路2. 中,必须分清是从左子树返回还是从右子树返回。因此设定一个辅助指针r,指向最近被访问过的结点。

/* 二叉链表后序非递归遍历代码 */
void PostOrder(BiTree T){
	InitStack(S);									// 初始化栈S
	p = T;											
	r = NULL;										// 辅助指针r
	while( p || !IsEmpty(S)){						// 栈不空或p不空时循环
		if (p){										// 判空操作 // p指针为非空,一路向左走
			Push(S, p);								// 当前结点入栈
			p = p -> lchild;						// p指针赋值当前结点的左孩子
		} else {									// p指针为空,向右子树走
			GetTop(S, p);							// 读栈顶元素,不是出栈操作
			if (p->rchild && p->rchild != r){		// 当前结点的右孩子不空且右孩子未遍历过
				p = p -> rchild;					// p指针指向当前结点的右孩子
				Push(S, p);							// 当前结点入栈
				p = p -> lchild;					// p指针指向当前结点的左孩子
			} else {								// 当前结点的右孩子为空或右孩子被遍历过
				Pop(S, p);							// 当前结点出栈
				visit(p -> data);					// 访问当前结点
				r = p;								// 辅助指针记录访问过的结点
				p = NULL;							// 结点访问完后,重置p指针
			}
		}
	}
}

层序遍历

图 6
有时候我们希望能一层一层的遍历二叉树,实现这个需求需要一个队列做为辅助
层序遍历的思路:

  1. 初始化一个辅助队列
  2. 根结点入队
  3. 若队列非空,则队头结点出队,访问该结点,并且其左右孩子插入队尾
  4. 重复3. 直到队列为空
/* 二叉链表的层序遍历 */
void LevelOrder(BiTree T){
	InitQueue(Q);					// 初始化队列
	BiTree p;						
	EnQueue(Q, T);					// 根结点入队列
	while( !IsEmpty(Q)){			// 判断队列是否为空
		DeQueue(Q, p);				// 队头结点出队列
		visit(p);					// 访问队头元素
		if ( p->lchild != NULL) 	// 判断左孩子是否不为空
			EnQueue(Q, p->lchild);	// 左孩子不空,入队列
		if ( p->rchild != NULL)		// 判断右孩子是否不为空
			EnQueue(Q, p->rchild);	// 右孩子不空,入队列
	}
}

由遍历序列—构造二叉树

若只能给出一棵二叉树的前/中/后/序遍历序列中的一种,不能唯一确定一棵二叉树。如果给出两种遍历序列,就有可能确定一棵二叉树

前序 + 中序遍历序列
后序 + 中序遍历序列
层序 + 中序遍历序列

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值