第5章 : 树与二叉树
1 二叉树的存储结构
1.1 顺序存储结构
#define MaxSize 100
struct TreeNode{
ElemType value; //结点中数据元素
bool isEmpty; //结点是否为空
};
TreeNode t[MaxSize];
1.2 链式存储结构
struct ElemType{
int value;
};
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild,*rchild; //左、右孩子指针
struct BiTNode *parent; //父节点指针
}BiTNode,*BiTree;
BiTree root = NULL;
root = (BiTree) malloc(sizeof(BiTNode));
root->data = {1};
root->lchild = NULL;
root->rchild = NULL;
BiTNode *p = (BiTNode *)malloc(sizeof(BiTNode));
p->data = {2};
p->lchild = NULL;
p->rchild = NULL;
root->rchild = p;
2 二叉树的遍历
2.1 先序遍历
void PreOrder(BiTree T){
if(T != NULL){
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
2.2 中序遍历
void InOrder(BiTree T){
if(T != NULL){
PreOrder(T->lchild);
visit(T);
PreOrder(T->rchild);
}
}
2.3 后序遍历
void PostOrder(BiTree T){
if(T != NULL){
PreOrder(T->lchild);
PreOrder(T->rchild);
visit(T);
}
}
2.4 层序遍历
//二叉树的结点(链式存储)
typedef struct BiTree{
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//链式队列结点
typedef struct LinkNode{
BiTNode *data; //保存指针而不是结点
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
void LeverOrder(BiTree T){
LinkQueue Q;
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); //右孩子入队
}
}
3 线索二叉树
3.1 线索二叉树结点
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag; //tag==0,表示指针指向孩子,tag==1,表示指针是“线索”
}ThreadNode,*ThreadTree;
3.2 中序线索化
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
ThreadNode *pre = NULL; //全局变量pre,指向当前访问结点的前驱
void CreateInThread(ThreadTree T){
pre = NULL;
if(T != NULL){ //非空二叉树才可以进行线索化
InThread(T) //中序线索化二叉树
if(pre->rchild == NULL)
pre->rchild = 1;
}
}
void InThread(ThreadNode T){
if(T != NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild == NULL){ //左子树为空,建立前驱线索
q->lchild = pre;
q->ltag = 1;
}
if(pre != NULL && pre->rchild == NULL){
pre->rchild = q; //建立前驱结点的后继线索
pre->rtag = 1;
}
pre = q;
}
3.3 先序线索化
为了避免先序线索化中左孩子与前驱线索冲突,故使用ltag
来进行区别
void PreThread(ThreadNode T){
if(T != NULL){
visit(T);
if(T->ltag == 0) //lchild不是前驱线索
InThread(T->lchild);
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild == NULL){ //左子树为空,建立前驱线索
q->lchild = pre;
q->ltag = 1;
}
if(pre != NULL && pre->rchild == NULL){
pre->rchild = q; //建立前驱结点的后继线索
pre->rtag = 1;
}
pre = q;
}
3.4 后序线索化
void PostThread(ThreadNode T){
if(T != NULL){
InThread(T->lchild);
InThread(T->rchild);
visit(T);
}
}
void visit(ThreadNode *q){
if(q->lchild == NULL){ //左子树为空,建立前驱线索
q->lchild = pre;
q->ltag = 1;
}
if(pre != NULL && pre->rchild == NULL){
pre->rchild = q; //建立前驱结点的后继线索
pre->rtag = 1;
}
pre = q;
}
4 中序线索二叉树
4.1 找中序后继
ThreadNode *FirstNode(ThreadNode *p){
//循环找到最左下结点
while(p->ltag == 0)
p = p->lchild;
return p;
}
ThreadNode *Nextnode(ThreadNode *p){
if(p->rtag == 0) return FirstNode(p->rchild);
else return p->rchild; //rtag==1直接返回后继线索
}
void InOrder(ThreadNode *T){
for(ThreadNode *p = FirstNode(T);p != NULL;p = Nextnode(p))
visit(p);
}
4.2 找中序前驱
ThreadNode *LastNode(ThreadNode *p){
//循环找到最右下结点
while(p->rtag == 0)
p = p->rchild;
return p;
}
ThreadNode *Prenode(ThreadNode *p){
if(p->ltag == 0) return FirstNode(p->lchild);
else return p->lchild; //rtag==1直接返回后继线索
}
//对中序线索二叉树进行逆向中序遍历
void RevInorder(ThreadNode *T){
for(ThreadNode *p = LastNode(T);p != NULL;p = Prenode(p))
visit(p);
}
5 树、森林
5.1 树的存储结构
5.1.1 双亲表示法(顺序存储)
#define MAX_TREE_SIZE 100
typedef struct{
ElemType data;
int parent;
}PTNode;
typedef struct {
PTNode nodes[MAX_TREE_SIZE];
int n;
}PTree;
5.1.2 孩子表示法(顺序+链式存储)
struct CTNode{
int child; //孩子结点在数组中的位置
struct CTNode *next; //下一个孩子
};
typedef struct{
ElemType data;
struct CTNode *firstChild; //第一个孩子
} CTBox;
typedef struct{
CTBox nodes[MAX_TREE_SIZE];
int n,r; //结点数和根的位置
}CTree;
6 并查集
6.1 并查集
#define SIZE 100
int UFSets[SIZE]; //集合元素数组(双亲指针数组)
//并查集的初始化
void Initial(int S[]){
for(int i = 0;i < size;i++){
S[i] = -1;
}
}
//Find操作(函数在并查集S中查找并返回包含元素x的🌲的根
int Find(int S[]){
while(S[x] >= 0)
x = S[x];
return x;
}
void Union(int S[],int Root1,int Root2){
if(Root1 == Root2)
return;
//将根Root2连接到另一根Root1的下面
S[Root2] = Root1;
}
6.2 对于Union
的优化:
:$\lfloor \log_2x+1 \rfloor$
故优化过后,Find
操作最坏时间复杂度为:
O
(
log
2
n
)
O(\log_2n)
O(log2n)
void Union(int S[],int Root1,int Root2){
if(Root1 == Root2)
return;
//将根Root2连接到另一根Root1的下面
if(S[Root2] > S[Root1]){ //Root2结点更少,因为数组值为负数,越大代表绝对值越小
S[Root1] += Root2; //累加结点总数
S[Root2] = Root1; //小树合并到大树
}
else{
S[Root2] += Root1;
S[Root1] = Root2;
}
S[Root2] = Root1;
}
6.3 对于Find
的优化
// Find “查”操作优化,先找到根结点,再进行“压缩路径”
int Find(int S[],int x){
int root = x;
while(S[root] >= 0)
root = S[root]; //循环找到根
while(x != root){ //压缩路径
int t = S[x]; //t指向x的父节点
S[x] = root; //x直接挂到根下
x = t;
}
return root; //返回结点编号
}