二叉树基础 C语言 MOOC学习笔记#2


内容来自浙大数据结构MOOC 本文仅为学习笔记

一、基本概念

  1. 树的度:所有结点的度中最大的度
  2. 结点的层次:规定根结点在第1层

二、树的表示

1.表示方法的困难之处

(1)由于每个结点的father和child个数不统一,在后续程序处理中出现麻烦
(2)如果尝试用一个统一的方法来表示所有的结点,
如:可以用child个数最大的结点的结构来表示所有的结点。
问题:会造成空间浪费

2. 改进的一种表示方式:child-sibling表示法

(1)对于每个Element: FirstChild指向第一个儿子,NextSibling指向下一个兄弟
(2)虽然有空间浪费,但空间浪费减少
(3)对于每个结点,都有两个分叉(左/右),一个为FirstChild,另一个为NextSibling,即为二叉树

三、二叉树基本理念

1. 基本概念

(1)与普通的度为2的树的区别:二叉树的左右分叉有区别

(2) 特殊二叉树

  • 斜二叉树(Skewed Binary Tree):每个结点都只有同一侧的分叉,构成了一个链表
  • 完美/满二叉树(Perfect/Full Binary Tree):所有的结点都有左和右
  • 完全二叉树(Complete Binary Tree): 结点的编号与满二叉树(从上至下、从左到右的顺序的)编号相同。即相对满二叉树来说,最后一层的树可以从右侧缺少一些。

2. 重要性质

(1)一个二叉树的第i层最多结点数为2^(i-1),i>=1
(2)深度为k的二叉树的最大结点总数为:2^k-1,k>=1
(3)对任何非空二叉树,n_0=叶结点(没有儿子)的个数,n_2=有2个儿子的结点的个数,n_0=(n_2) +1
*证明:从边和点的个数的关系出发

3. 二叉树的基本操作

(1)Boolean IsEmpty(BinTree BT)

判断二叉树是否为空

(2)void Traversal(BinTree BT)

遍历,按某顺序访问每个结点
常用遍历方法:

  • void PreOrderTraversal(BinTree BT):先序,根-左子树-右子树
  • void InOrderTraversal(BinTree BT):中序,左子树-根-右子树
  • void PostOrderTraversal(BinTree BT):后序,左子树-右子树-根
  • void LevelOrderTraversal(BinTree BT):层次遍历,从上到下,从左到右

(3) BinTree CreatBinTree()

创造一个二叉树

四、二叉树的存储方式

1. 顺序存储结构

用数组,从上至下、从左到右的顺序存储(从1开始)

对于完全二叉树而言:

特点:
(1)对于任何非根结点,它的父结点序号为:floor(i/2)
(2)结点i的左孩子结点序号为2i,右孩子的结点序号为2i+1

对于一般的二叉树:

补充为一个完全二叉树。留下空缺。会造成空间浪费。

2. 链表存储

typedef struct TreeNode* BinTree;
typedef BinTree Position;
struct TreeNode{
	ElementType Data;
	BinTree Left;
	BinTree Right;
}

五、二叉树的遍历

核心问题:将二叉树这种二维结构线性化

1. 先序遍历

根-左-右

void PreOrderTraversal(BinTree BT){
	if(BT){ //to check if the tree is empty 
		printf("%d",BT->Data);
		PreOrderTraversal(BT->Left);
		PreOrderTraversal(BT->Right);
	}
}

2. 中序遍历

左-根-右

void InOrderTraversal(BinTree BT){
	if(BT){ //to check if the tree is empty 
		PreOrderTraversal(BT->Left);
		printf("%d",BT->Data);
		PreOrderTraversal(BT->Right);
	}
}

3. 后序遍历

左-右-根

void PostOrderTraversal(BinTree BT){
	if(BT){ //to check if the tree is empty 
		PreOrderTraversal(BT->Left);
		PreOrderTraversal(BT->Right);
		printf("%d",BT->Data);
	}
}

总结:以上三种结点走的路径是一样的,都是从入口开始往左-根-右-根;
只是访问各结点的时机不同。

*堆栈而不用递归的方法

- 中序遍历

实现思路:

  1. 遇见一个结点,就把它压栈,并去遍历它的左子树
  2. 左子树遍历结束后,从栈顶弹出这个结点并访问它
  3. 然后按其右指针再去中序遍历该结点的右子树
void InOrderTraversal(BinTree BT){
	BinTree T=BT; //not to change BT
	Stack S=CreatStack(MaxSize); //initialize stack S
	while(T || IsEmpty(S){ //T and stack are not empty
		while(T){ // push the left tree into the stack
			Push(S,T); 
			T=T->Left; 
		}
		if(!IsEmpty(S){
			T=Pop(S); // pop the element if all its left tree has been pushed into the stack
			printf("%5d",T->Data);
			T=T->Right; // turn to the right tree
		}
	}
}

-先序遍历&后序遍历

从前面的总结来看,先、中、后的走过的路径是一样的,故仅需在中序遍历的基础上稍加调整顺序即可

4. 层次遍历

队列实现:
遍历从根结点开始,根结点入队,开始执行循环:
结点出队,访问该结点,左右儿子入队

void LevelOrderTraversal(BinTree BT){
	Queue Q; BinTree T;
	if (!BT) return; //If it tree is empty, return;
	Q=CreatQueue(MaxSize); //create a queue
	AddQ(Q,BT);
	while(!IsEmptyQ(Q)){
		T=DeleteQ(Q);
		printf("%d\n",T->Data);
		// add the left and right child into the queue
		if (T->Left) {AddQ(Q,T->Left);}
		if (T->Right) {AddQ(Q,T->Right);}
	}
}

五、二叉树的应用

1. 求二叉树的高度

Height =max(HL,HR)+1
后序遍历(左-右-根),递归

int PostOrderGetHeight(BinTree BT){
	int HL,HR,MaxH;
	if (BT){
		HL=PostOrderGetHeight(BT->Left);
		// get the height of the left tree
		HR=PostOrderGetHeight(BT->Right);
		// get the height of the right tree
		MaxH=(HL>HR)? HL:HR;
		return (MaxH+1);
	}
	else return 0; //the height of the empty tree is 0
}

2. 二元运算表达式树

叶结点是运算变量,非叶结点为运算符号
三种遍历得到三种不同的访问结果:x序遍历得到x缀表达式
*中缀表达式会受到运算优先级的影响
-> 改进方法:添加括号

3. 由两种遍历序列确定二叉树

必须要有中序遍历才可确定
思路:

  1. 根据先序遍历第一个结点确定根结点
  2. 根据结点在中序遍历中分割出左右两个子序列
  3. 对左右子树分别递归使用相同的方法继续分解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值