王道学习
考纲内容
(一)树的基本概念
(二)二叉树
二叉树的定义及其主要特征;二叉树的顺序存储结构和链式存储结构;二叉树的遍历;线索二叉树的基本概念和构造
(三)树、森林
树的存储结构;森林与二叉树的转换;树和森林的遍历
(四)树与二叉树的应用
哈夫曼(Huffman)树和哈夫曼编码;并查集及其应用
知识框架
复习提示
本章内容多以选择题或综合题的形式考查,但统考也会出涉及树遍历相关的算法题。树和二叉树的性质、遍历操作、转换、存储结构和操作特性等,满二叉树、完全二叉树、线索二叉树、哈夫曼树的定义和性质,都是选择题必然会涉及的内容。
5.1 树的基本概念
5.1.1 树的定义
树是n (n≥0)个节点的有限集。当n=0时,称为空树。在任意一棵非空树中应满足:
1)有且仅有一个特定的称为根的结点。
2)当n>1时,其余节点可分为m (m>0)个互不相交的有限集T,T2,…,Tm,其中每个集合本身又是一棵树,并且称为根的子树。
显然,树的定义是递归的,即在树的定义中又用到了其自身,树是一种递归的数据结构。树作为一种逻辑结构,同时也是一种分层结构,具有以下两个特点:
1)树的根结点没有前驱,除根结点外的所有结点有且只有一个前驱。
2)树中所有结点可以有零个或多个后继。
树适合于表示具有层次结构的数据。树中的某个结点(除根结点外)最多只和上一层的一个结点(即其父结点)有直接关系,根结点没有直接上层结点,因此在n个结点的树中有n-1条边。而树中每个结点与其下一层的零个或多个结点(即其子女结点)有直接关系。
5.1.2 基本术语
结点的深度是从根结点开始自顶向下逐层累加的。
结点的高度是从叶结点开始自底向上逐层累加的。
注意:由于树中的分支是有向的,即从双亲指向孩子,所以树中的路径是从上向下的,同一双亲的两个孩子之间不存在路径。
7)森林。森林是m(m≥0)棵互不相交的树的集合。森林的概念与树的概念十分相近,因为只要把树的根结点删去就成了森林。反之,只要给m棵独立的树加上一个结点,并把这m棵树作为该结点的子树,则森林就变成了树。
5.1.3 树的性质
5.1.4 本节试题精选
5.2 二叉树的概念
5.2.1 二叉树的定义及其主要特性
1、二叉树的定义
二叉树与度为2的有序树的区别:
①度为2的树至少有3个结点,而二叉树可以为空。
②度为2的有序树的孩子的左右次序是相对于另一孩子而言的,若某个结点只有一个孩子,则这个孩子就无须区分其左右次序,而二叉树无论其孩子数是否为2,均需确定其左右次序,即二叉树的结点次序不是相对于另一结点而言,而是确定的。
2、几个特殊的二叉树
正则二叉树。树中每个分支结点都有2个孩子,即树中只有度为0或2的结点。
3、二叉树的性质
5.2.2 二叉树的存储结构
1、顺序存储
//二叉树--顺序存储--适合完全二叉树
#include <stdio.h>
#define MAXSIZE 100
struct TreeNode{
//结点中的数据元素
int value;
//判断结点是否为空 0为空 1不为空
int isEmpty;
};
typedef struct TreeNode TreeNode;
/*
初始化tree
*/
void initTree(TreeNode *tree){
int i=1;
for(;i<=MAXSIZE-1;i++){
tree[i].isEmpty=0;
}
}
/*
输出初始化的结果
*/
void printfInitTree(TreeNode tree[]){
int i=1;
for(;i<=MAXSIZE-1;i++){
printf("%d,",tree[i].isEmpty);
}
}
int main(){
TreeNode tree[MAXSIZE];
initTree(tree);
printfInitTree(tree);
tree[1].value=1;
tree[1].isEmpty=1;
tree[2].value=2;
tree[2].isEmpty=1;
tree[3].value=3;
tree[3].isEmpty=1;
tree[4].value=4;
tree[4].isEmpty=1;
tree[5].value=5;
tree[5].isEmpty=1;
tree[6].value=6;
tree[6].isEmpty=1;
tree[7].value=7;
tree[7].isEmpty=1;
tree[8].value=8;
tree[8].isEmpty=1;
tree[9].value=9;
tree[9].isEmpty=1;
tree[10].value=10;
tree[10].isEmpty=1;
tree[11].value=11;
tree[11].isEmpty=1;
tree[12].value=12;
tree[12].isEmpty=1;
return 0;
}
2、链式存储
5.2.3 本节试题精选
5.3 二叉树的遍历和线索二叉树
5.3.1 二叉树的遍历
二叉树的遍历是指按某条搜索路径访问树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。由于二叉树是一种非线性结构,每个结点都可能有两棵子树,因而需要寻找一种规律,以便使二叉树上的结点能排列在一个线性队列上,进而便于遍历。
由二叉树的递归定义可知,遍历一棵二叉树便要决定对根结点N、左子树L和右子树R的访问顺序。按照先遍历左子树再遍历右子树的原则,常见的遍历次序有先序(NLR)、中序(LNR)和后序(LRN)三种遍历算法,其中“序”指的是根结点在何时被访问。
1、先序遍历
2、中序遍历
3、后序遍历
上述三种遍历算法中,递归遍历左、右子树的顺序都是固定的,只是访问根结点的顺序不同。不管采用哪种遍历算法,每个结点都访问一次且仅访问一次,故时间复杂度都是O(n)。在递归遍历中,递归工作栈的栈深恰好为树的深度,所以在最坏情况下,二叉树是有n个结点且深度为n的单支树,遍历算法的空间复杂度为O(n)。
4、递归算法和非递归算法的转换
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct BiTNode{
//数据域
int value;
//左孩子
struct BiTNode *lChild;
//右孩子
struct BiTNode *rChild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
struct LNode{
BiTNode* data;
struct LNode *next;
};
typedef struct LNode LNode;
typedef struct LNode* SqStack;
/*
初始化链式栈:
1、头指针指向头结点
2、头结点的next初始化为NULL
返回一个SqStack型变量:
具体的值--成功
NULL--失败
*/
SqStack initStack(SqStack sqStack){
LNode* newLNode=(LNode*)malloc(sizeof(LNode));
if(newLNode==NULL){
return NULL;
}
(*newLNode).next=NULL;
sqStack=newLNode;
return sqStack;
}
/*
判空栈
返回值
0--空
1--非空
*/
int stackEmpty(SqStack sqStack){
if(sqStack==NULL){
return 0;
}
if((*sqStack).next!=NULL){
return 1;
}else{
return 0;
}
}
/*
进栈
返回值
0--失败
1--成功
*/
int push(SqStack sqStack,BiTNode* data){
//指向头结点
LNode *tmp=sqStack;
if(tmp==NULL){
return 0;
}
LNode *newLNode=(LNode *)malloc(sizeof(LNode));
if(newLNode==NULL){
return 0;
}
(*newLNode).data=data;
(*newLNode).next=(*tmp).next;
(*tmp).next=newLNode;
return 1;
}
/*
出栈
返回值
0--失败
1--成功
*/
BiTNode * pop(SqStack sqStack){
//指向头结点
LNode *tmp=sqStack;
//指向第一个结点
LNode *p=(*tmp).next;
BiTNode *data=(*p).data;
(*tmp).next=(*p).next;
free(p);
return data;
}
/*
初始化tree
*/
BiTNode* initTree(BiTree root){
root=NULL;
return root;
}
/*
构造树
*/
BiTNode* constructTree(BiTree root){
BiTNode *p11=(BiTNode *)malloc(sizeof(BiTNode));
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
BiTNode *p12=(BiTNode *)malloc(sizeof(BiTNode));
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
BiTNode *p5=(BiTNode *)malloc(sizeof(BiTNode));
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
BiTNode *p6=(BiTNode *)malloc(sizeof(BiTNode));
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
BiTNode *p7=(BiTNode *)malloc(sizeof(BiTNode));
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
BiTNode *p2=(BiTNode *)malloc(sizeof(BiTNode));
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
BiTNode *p3=(BiTNode *)malloc(sizeof(BiTNode));
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
BiTNode *p1=(BiTNode *)malloc(sizeof(BiTNode));
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
/*
中序遍历--左根右
*/
void inOrder(BiTree biTree){
if(biTree!=NULL){
inOrder((*biTree).lChild);
printf("%d ",(*biTree).value);
inOrder((*biTree).rChild);
}
}
int main(){
BiTNode *q=NULL;
BiTree root;
SqStack sqStack;
//构造树
root=initTree(root);
root=constructTree(root);
//构造栈
sqStack=initStack(sqStack);
printf("\n");
printf("中序:");
inOrder(root);
printf("\n");
printf("中序的非递归代码:");
while(root!=NULL || 1==stackEmpty(sqStack)){
if(root!=NULL){
push(sqStack,root);
root=root->lChild;
}else{
root=pop(sqStack);
printf("%d ",root->value);
root=root->rChild;
}
}
return 0;
}
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct BiTNode{
//数据域
int value;
//左孩子
struct BiTNode *lChild;
//右孩子
struct BiTNode *rChild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
struct LNode{
BiTNode* data;
struct LNode *next;
};
typedef struct LNode LNode;
typedef struct LNode* SqStack;
/*
初始化链式栈:
1、头指针指向头结点
2、头结点的next初始化为NULL
返回一个SqStack型变量:
具体的值--成功
NULL--失败
*/
SqStack initStack(SqStack sqStack){
LNode* newLNode=(LNode*)malloc(sizeof(LNode));
if(newLNode==NULL){
return NULL;
}
(*newLNode).next=NULL;
sqStack=newLNode;
return sqStack;
}
/*
判空栈
返回值
0--空
1--非空
*/
int stackEmpty(SqStack sqStack){
if(sqStack==NULL){
return 0;
}
if((*sqStack).next!=NULL){
return 1;
}else{
return 0;
}
}
/*
进栈
返回值
0--失败
1--成功
*/
int push(SqStack sqStack,BiTNode* data){
//指向头结点
LNode *tmp=sqStack;
if(tmp==NULL){
return 0;
}
LNode *newLNode=(LNode *)malloc(sizeof(LNode));
if(newLNode==NULL){
return 0;
}
(*newLNode).data=data;
(*newLNode).next=(*tmp).next;
(*tmp).next=newLNode;
return 1;
}
/*
出栈
返回值
0--失败
1--成功
*/
BiTNode * pop(SqStack sqStack){
//指向头结点
LNode *tmp=sqStack;
//指向第一个结点
LNode *p=(*tmp).next;
BiTNode *data=(*p).data;
(*tmp).next=(*p).next;
free(p);
return data;
}
/*
初始化tree
*/
BiTNode* initTree(BiTree root){
root=NULL;
return root;
}
/*
构造树
*/
BiTNode* constructTree(BiTree root){
BiTNode *p11=(BiTNode *)malloc(sizeof(BiTNode));
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
BiTNode *p12=(BiTNode *)malloc(sizeof(BiTNode));
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
BiTNode *p5=(BiTNode *)malloc(sizeof(BiTNode));
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
BiTNode *p6=(BiTNode *)malloc(sizeof(BiTNode));
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
BiTNode *p7=(BiTNode *)malloc(sizeof(BiTNode));
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
BiTNode *p2=(BiTNode *)malloc(sizeof(BiTNode));
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
BiTNode *p3=(BiTNode *)malloc(sizeof(BiTNode));
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
BiTNode *p1=(BiTNode *)malloc(sizeof(BiTNode));
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
/*
先序遍历--根左右
*/
void preOrder(BiTree biTree){
if(biTree!=NULL){
printf("%d ",(*biTree).value);
preOrder((*biTree).lChild);
preOrder((*biTree).rChild);
}
}
int main(){
BiTNode *q=NULL;
BiTree root;
SqStack sqStack;
//构造树
root=initTree(root);
root=constructTree(root);
//构造栈
sqStack=initStack(sqStack);
printf("\n");
printf("先序:");
preOrder(root);
printf("\n");
printf("先序的非递归代码:");
while(root!=NULL || 1==stackEmpty(sqStack)){
if(root!=NULL){
printf("%d ",root->value);
push(sqStack,root);
root=root->lChild;
}else{
root=pop(sqStack);
root=root->rChild;
}
}
return 0;
}
后序遍历的非递归实现是三种遍历方法中最难的。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。
后序非递归遍历算法的思路分析:从根结点开始,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,但是此时不能出栈并访问,因为如果其有右子树,还需按相同的规则对其右子树进行处理。直至上述操作进行不下去,若栈顶元素想要出栈被访问,要么右子树为空,要么右子树刚被访问完(此时左子树早已访问完),这样就保证了正确的访问顺序。
算法思想:
1、先沿根结点,依次入栈,直到左孩子为空
2、读取栈顶元素;如果其右孩子不空且未被访问过,将右子树转执行
3、否则,栈顶元素出栈并访问
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct BiTNode{
//数据域
int value;
//左孩子
struct BiTNode *lChild;
//右孩子
struct BiTNode *rChild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
struct LNode{
BiTNode* data;
struct LNode *next;
};
typedef struct LNode LNode;
typedef struct LNode* SqStack;
/*
初始化链式栈:
1、头指针指向头结点
2、头结点的next初始化为NULL
返回一个SqStack型变量:
具体的值--成功
NULL--失败
*/
SqStack initStack(SqStack sqStack){
LNode* newLNode=(LNode*)malloc(sizeof(LNode));
if(newLNode==NULL){
return NULL;
}
(*newLNode).next=NULL;
sqStack=newLNode;
return sqStack;
}
/*
判空栈
返回值
0--空
1--非空
*/
int stackEmpty(SqStack sqStack){
if(sqStack==NULL){
return 0;
}
if((*sqStack).next!=NULL){
return 1;
}else{
return 0;
}
}
/*
进栈
返回值
0--失败
1--成功
*/
int push(SqStack sqStack,BiTNode* data){
//指向头结点
LNode *tmp=sqStack;
if(tmp==NULL){
return 0;
}
LNode *newLNode=(LNode *)malloc(sizeof(LNode));
if(newLNode==NULL){
return 0;
}
(*newLNode).data=data;
(*newLNode).next=(*tmp).next;
(*tmp).next=newLNode;
return 1;
}
/*
出栈
返回值
0--失败
1--成功
*/
BiTNode * pop(SqStack sqStack){
//指向头结点
LNode *tmp=sqStack;
//指向第一个结点
LNode *p=(*tmp).next;
BiTNode *data=(*p).data;
(*tmp).next=(*p).next;
free(p);
return data;
}
/*
获取栈顶元素
返回值
0--失败
1--成功
*/
BiTNode * getTop(SqStack sqStack){
//指向头结点
LNode *tmp=sqStack;
//指向第一个结点
LNode *p=(*tmp).next;
BiTNode *data=(*p).data;
return data;
}
/*
初始化tree
*/
BiTNode* initTree(BiTree root){
root=NULL;
return root;
}
/*
构造树
*/
BiTNode* constructTree(BiTree root){
BiTNode *p11=(BiTNode *)malloc(sizeof(BiTNode));
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
BiTNode *p12=(BiTNode *)malloc(sizeof(BiTNode));
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
BiTNode *p5=(BiTNode *)malloc(sizeof(BiTNode));
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
BiTNode *p6=(BiTNode *)malloc(sizeof(BiTNode));
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
BiTNode *p7=(BiTNode *)malloc(sizeof(BiTNode));
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
BiTNode *p2=(BiTNode *)malloc(sizeof(BiTNode));
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
BiTNode *p3=(BiTNode *)malloc(sizeof(BiTNode));
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
BiTNode *p1=(BiTNode *)malloc(sizeof(BiTNode));
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
/*
后序遍历--左右根
*/
void postOrder(BiTree biTree){
if(biTree!=NULL){
postOrder((*biTree).lChild);
postOrder((*biTree).rChild);
printf("%d ",(*biTree).value);
}
}
int main(){
BiTNode *q=NULL;
BiTNode *r=NULL;
BiTree root;
SqStack sqStack;
//构造树
root=initTree(root);
root=constructTree(root);
//构造栈
sqStack=initStack(sqStack);
printf("\n");
printf("后序:");
postOrder(root);
printf("\n");
printf("后序的非递归代码:");
while(root!=NULL || 1==stackEmpty(sqStack)){
if(root!=NULL){
push(sqStack,root);
root=root->lChild;
}else{
//获取栈顶元素
root=getTop(sqStack);
//右子树存在,且未被访问过
//可以看出root指针指向栈顶元素时,如果元素存在右子树,且r指针保留了访问过的右子树,就会阻止访问,
//直接使栈顶元素出栈这也是后序遍历不同于先序、中序,所要特别处理的地方。
if(root->rChild!=NULL&&root->rChild!=r){
root=root->rChild;
}else{
//弹出结点并访问
root=pop(sqStack);
printf("%d ",root->value);
//记录最近访问的结点
r=root;
//结点访问完,重置指针
root=NULL;
}
}
}
return 0;
}
5、层次遍历
6、由遍历序列构造二叉树
5.3.2 代码阶段性总结
以下代码所用的二叉树如下图
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct BiTNode{
//数据域
int value;
//左孩子
struct BiTNode *lChild;
//右孩子
struct BiTNode *rChild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
struct LinkNode{
BiTNode* data;
struct LinkNode *next;
};
typedef struct LinkNode LinkNode;
struct LinkQueue{
LinkNode *front;
LinkNode *rear;
};
typedef struct LinkQueue LinkQueue;
/*
初始化tree
*/
BiTNode* initTree(BiTree root){
root=NULL;
return root;
}
/*
构造树
*/
BiTNode* constructTree(BiTree root){
BiTNode *p11=(BiTNode *)malloc(sizeof(BiTNode));
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
BiTNode *p12=(BiTNode *)malloc(sizeof(BiTNode));
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
BiTNode *p5=(BiTNode *)malloc(sizeof(BiTNode));
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
BiTNode *p6=(BiTNode *)malloc(sizeof(BiTNode));
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
BiTNode *p7=(BiTNode *)malloc(sizeof(BiTNode));
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
BiTNode *p2=(BiTNode *)malloc(sizeof(BiTNode));
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
BiTNode *p3=(BiTNode *)malloc(sizeof(BiTNode));
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
BiTNode *p1=(BiTNode *)malloc(sizeof(BiTNode));
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
/*
先序遍历--根左右
*/
void preOrder(BiTree biTree){
if(biTree!=NULL){
printf("%d ",(*biTree).value);
preOrder((*biTree).lChild);
preOrder((*biTree).rChild);
}
}
/*
中序遍历--左根右
*/
void inOrder(BiTree biTree){
if(biTree!=NULL){
inOrder((*biTree).lChild);
printf("%d ",(*biTree).value);
inOrder((*biTree).rChild);
}
}
/*
后序遍历--左右根
*/
void postOrder(BiTree biTree){
if(biTree!=NULL){
postOrder((*biTree).lChild);
postOrder((*biTree).rChild);
printf("%d ",(*biTree).value);
}
}
/*
树的深度
*/
int treeDepth(BiTree root){
if(root==NULL){
return 0;
}else{
int l=treeDepth((*root).lChild);
int r=treeDepth((*root).rChild);
return l>r?l+1:r+1;
}
}
/*
初始化队列
返回值
0--失败
1--成功
*/
int initQueue(LinkQueue *queue){
LinkNode *newNode=(LinkNode *)malloc(sizeof(LinkNode));
if(newNode==NULL){
return 0;
}
(*newNode).next=NULL;
(*queue).front=newNode;
(*queue).rear=newNode;
}
/*
入队操作
返回值
0--失败
1--成功
*/
int enQueue(LinkQueue *queue,BiTNode* data){
LinkNode *newNode=(LinkNode *)malloc(sizeof(LinkNode));
if(newNode==NULL){
return 0;
}
(*newNode).data=data;
(*newNode).next=NULL;
(*queue).rear->next=newNode;
(*queue).rear=newNode;
return 1;
}
/*
出队操作
*/
BiTNode* deQueue(LinkQueue *queue){
LinkNode *front=(*queue).front;
LinkNode *rear=(*queue).rear;
//被出队的结点--头结点的下一个结点
LinkNode *tmpNode=(*front).next;
BiTNode* result=(*tmpNode).data;
(*front).next=(*tmpNode).next;
if((*tmpNode).next==NULL){
(*queue).rear=front;
}
free(tmpNode);
return result;
}
/*
判断队列是否为空
1--为空
0--不为空
*/
int queueEmpty(LinkQueue queue){
if(queue.front==queue.rear){
return 1;
}else{
return 0;
}
}
/*
层次遍历
*/
void levelOrder(BiTree root){
if(root==NULL){
return ;
}
LinkQueue linkQueue;
initQueue(&linkQueue);
enQueue(&linkQueue,root);
BiTNode* p;
while(queueEmpty(linkQueue)==0){
p=deQueue(&linkQueue);
printf("%d ",(*p).value);
if((*p).lChild!=NULL){
enQueue(&linkQueue,(*p).lChild);
}
if((*p).rChild!=NULL){
enQueue(&linkQueue,(*p).rChild);
}
}
}
BiTNode *tmpResult=NULL;
/*
利用先序遍历,找到某一节点,并赋值给tmpResult
*/
void preOrderTwo(BiTree biTree){
if(biTree!=NULL){
if((*biTree).value==5){
tmpResult=biTree;
}
preOrderTwo((*biTree).lChild);
preOrderTwo((*biTree).rChild);
}
}
BiTNode *q1=NULL;
/*
中序前驱
q1-->当前被访问的结点
pre-->上一个被访问的结点
target-->求target结点的前驱
只要target==q1,pre就是target的前驱结点
最后将pre赋值给tmpResult
*/
void inPreBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
inPreBiTNode((*biTree).lChild,target);
pre=q1;
q1=biTree;
if(q1==target){
tmpResult=pre;
}
inPreBiTNode((*biTree).rChild,target);
}
}
/*
中序后继
q1-->当前被访问的结点
pre-->上一个被访问的结点
target-->求target结点的前驱
只要target==pre,q1就是target的后继结点
最后将q赋值给tmpResult
*/
void inNextBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
inNextBiTNode((*biTree).lChild,target);
pre=q1;
q1=biTree;
if(pre==target){
tmpResult=q1;
}
inNextBiTNode((*biTree).rChild,target);
}
}
BiTNode *q2=NULL;
/*
先序前驱
*/
void prePreBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
pre=q2;
q2=biTree;
if(q2==target){
tmpResult=pre;
}
prePreBiTNode((*biTree).lChild,target);
prePreBiTNode((*biTree).rChild,target);
}
}
/*
先序后继
*/
void preNextBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
pre=q2;
q2=biTree;
if(pre==target){
tmpResult=q2;
}
preNextBiTNode((*biTree).lChild,target);
preNextBiTNode((*biTree).rChild,target);
}
}
BiTNode *q3=NULL;
/*
后序前驱
*/
void postPreBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
postPreBiTNode((*biTree).lChild,target);
postPreBiTNode((*biTree).rChild,target);
pre=q3;
q3=biTree;
if(q3==target){
tmpResult=pre;
}
}
}
/*
后序后继
*/
void postNextBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
postNextBiTNode((*biTree).lChild,target);
postNextBiTNode((*biTree).rChild,target);
pre=q3;
q3=biTree;
if(pre==target){
tmpResult=q3;
}
}
}
int main(){
BiTNode *q=NULL;
BiTree root;
root=initTree(root);
root=constructTree(root);
printf("先序:");
preOrder(root);
printf("\n");
printf("中序:");
inOrder(root);
printf("\n");
printf("后序:");
postOrder(root);
printf("\n");
printf("深度:");
printf("%d",treeDepth(root));
printf("\n");
printf("层次:");
levelOrder(root);
printf("\n");
preOrderTwo(root);
printf("\n");
inPreBiTNode(root,tmpResult);
printf("结点5的中序前驱是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
inNextBiTNode(root,tmpResult);
printf("结点5的中序后继是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
prePreBiTNode(root,tmpResult);
printf("结点5的先序前驱是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
preNextBiTNode(root,tmpResult);
printf("结点5的先序后继是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
postPreBiTNode(root,tmpResult);
printf("结点5的后序前驱是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
postNextBiTNode(root,tmpResult);
printf("结点5的后序后继是:%d",(*tmpResult).value);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct BiTNode{
int value;
struct BiTNode *lChild;
struct BiTNode *rChild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
struct LinkNode{
BiTNode *data;
struct LinkNode *next;
};
typedef struct LinkNode LinkNode;
struct LinkQueue{
LinkNode *front;
LinkNode *rear;
};
typedef struct LinkQueue LinkQueue;
BiTNode* initTree(BiTree root){
root=NULL;
return root;
}
BiTNode* constructTree(BiTree root){
BiTNode *p11=(BiTNode *)malloc(sizeof(BiTNode));
p11->value=11;
p11->lChild=NULL;
p11->rChild=NULL;
BiTNode *p12=(BiTNode *)malloc(sizeof(BiTNode));
p12->value=12;
p12->lChild=NULL;
p12->rChild=NULL;
BiTNode *p5=(BiTNode *)malloc(sizeof(BiTNode));
p5->value=5;
p5->lChild=NULL;
p5->rChild=p11;
BiTNode *p6=(BiTNode *)malloc(sizeof(BiTNode));
p6->value=6;
p6->lChild=p12;
p6->rChild=NULL;
BiTNode *p7=(BiTNode *)malloc(sizeof(BiTNode));
p7->value=7;
p7->lChild=NULL;
p7->rChild=NULL;
BiTNode *p2=(BiTNode *)malloc(sizeof(BiTNode));
p2->value=2;
p2->lChild=NULL;
p2->rChild=p5;
BiTNode *p3=(BiTNode *)malloc(sizeof(BiTNode));
p3->value=3;
p3->lChild=p6;
p3->rChild=p7;
BiTNode *p1=(BiTNode *)malloc(sizeof(BiTNode));
p1->value=1;
p1->lChild=p2;
p1->rChild=p3;
root=p1;
return root;
}
void preOrder(BiTree biTree){
if(biTree!=NULL){
printf("%d",biTree->value);
preOrder(biTree->lChild);
preOrder(biTree->rChild);
}
}
void inOrder(BiTree biTree){
if(biTree!=NULL){
inOrder(biTree->lChild);
printf("%d",biTree->value);
inOrder(biTree->rChild);
}
}
void postOrder(BiTree biTree){
if(biTree!=NULL){
postOrder(biTree->lChild);
postOrder(biTree->rChild);
printf("%d",biTree->value);
}
}
int treeDepth(BiTree root){
if(root==NULL){
return 0;
}else{
int l=treeDepth(root->lChild);
int r=treeDepth(root->rChild);
return l>r?l+1:r+1;
}
}
int initQueue(LinkQueue *queue){
LinkNode *newNode=(LinkNode *)malloc(sizeof(LinkNode));
if(newNode==NULL){
return 0;
}
newNode->next=NULL;
queue->front=newNode;
queue->rear=newNode;
return 1;
}
int enQueue(LinkQueue *queue,BiTNode *data){
LinkNode *newNode=(LinkNode *)malloc(sizeof(LinkNode));
if(newNode==NULL){
return 0;
}
newNode->data=data;
newNode->next=NULL;
queue->rear->next=newNode;
queue->rear=newNode;
return 1;
}
int queueEmpty(LinkQueue queue){
if(queue.front==queue.rear){
return 1;
}else{
return 0;
}
}
BiTNode* deQueue(LinkQueue *queue){
LinkNode *front=queue->front;
LinkNode *rear=queue->rear;
LinkNode *tmpNode=front->next;
BiTNode* result=tmpNode->data;
front->next=tmpNode->next;
if(tmpNode->next==NULL){
queue->rear=front;
}
free(tmpNode);
return result;
}
void levelOrder(BiTree root){
if(root==NULL){
return ;
}
LinkQueue linkQueue;
initQueue(&linkQueue);
enQueue(&linkQueue,root);
BiTNode* p;
while(queueEmpty(linkQueue)==0){
p=deQueue(&linkQueue);
printf("%d ",p->value);
if(p->lChild!=NULL){
enQueue(&linkQueue,p->lChild);
}
if(p->rChild!=NULL){
enQueue(&linkQueue,p->rChild);
}
}
}
BiTNode *tmpResult=NULL;
void preOrderTwo(BiTree biTree){
if(biTree!=NULL){
if(biTree->value==5){
tmpResult=biTree;
}
preOrderTwo(biTree->lChild);
preOrderTwo(biTree->rChild);
}
}
BiTNode *q1=NULL;
void inPreBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
inPreBiTNode(biTree->lChild,target);
pre=q1;
q1=biTree;
if(q1==target){
tmpResult=pre;
}
inPreBiTNode(biTree->rChild,target);
}
}
void inNextBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
inNextBiTNode(biTree->lChild,target);
pre=q1;
q1=biTree;
if(pre==target){
tmpResult=q1;
}
inNextBiTNode(biTree->rChild,target);
}
}
BiTNode *q2=NULL;
void prePreBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
pre=q2;
q2=biTree;
if(q2==target){
tmpResult=pre;
}
prePreBiTNode(biTree->lChild,target);
prePreBiTNode(biTree->rChild,target);
}
}
void preNextBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
pre=q2;
q2=biTree;
if(pre==target){
tmpResult=q2;
}
preNextBiTNode(biTree->lChild,target);
preNextBiTNode(biTree->rChild,target);
}
}
BiTNode *q3=NULL;
void postPreBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
postPreBiTNode(biTree->lChild,target);
postPreBiTNode(biTree->rChild,target);
pre=q3;
q3=biTree;
if(q3==target){
tmpResult=pre;
}
}
}
void postNextBiTNode(BiTree biTree,BiTNode *target){
BiTNode *pre=NULL;
if(biTree!=NULL){
postNextBiTNode(biTree->lChild,target);
postNextBiTNode(biTree->rChild,target);
pre=q3;
q3=biTree;
if(pre==target){
tmpResult=q3;
}
}
}
int main(){
BiTNode *q=NULL;
BiTree root;
root=initTree(root);
root=constructTree(root);
printf("先序:");
preOrder(root);
printf("\n");
printf("中序:");
inOrder(root);
printf("\n");
printf("后序:");
postOrder(root);
printf("\n");
printf("深度:");
printf("%d",treeDepth(root));
printf("\n");
printf("层次:");
levelOrder(root);
printf("\n");
preOrderTwo(root);
printf("\n");
inPreBiTNode(root,tmpResult);
printf("结点5的中序前驱是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
inNextBiTNode(root,tmpResult);
printf("结点5的中序后继是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
prePreBiTNode(root,tmpResult);
printf("结点5的先序前驱是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
preNextBiTNode(root,tmpResult);
printf("结点5的先序后继是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
postPreBiTNode(root,tmpResult);
printf("结点5的后序前驱是:%d",(*tmpResult).value);
printf("\n");
preOrderTwo(root);
printf("\n");
postNextBiTNode(root,tmpResult);
printf("结点5的后序后继是:%d",(*tmpResult).value);
return 0;
}
5.3.3 线索二叉树
1、线索二叉树的基本概念
遍历二叉树是以一定的规则将二叉树中的结点排列成一个线性序列,从而得到几种遍历序列,使得该序列中的每个结点(第一个和最后一个结点除外)都有一个直接前驱和直接后继。
2、线索二叉树的构造
右指针存放的是后继,后继的确定是上一个结点pre确定,上一个结点早就遍历完了,所以不会出现循环现象
左指针存放的是前驱,先序的左是第二个遍历,如果左指针存放了根,接下来遍历左子树,就会造成循环;而中序和后序是先遍历左,所以不会造成循环
3、线索二叉树找前驱/后继
5.3.4 线索二叉树代码
中序线索二叉树
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct ThreadNode{
//数据域
int value;
//左孩子
struct ThreadNode *lChild;
//右孩子
struct ThreadNode *rChild;
//1--前驱结点 0--左孩子
int ltag;
//1--后继结点 0--右孩子
int rtag;
};
typedef struct ThreadNode ThreadNode;
typedef struct ThreadNode* ThreadTree;
/*
初始化tree
*/
ThreadTree* initTree(ThreadTree root){
root=NULL;
return root;
}
/*
构造树
*/
ThreadTree* constructTree(ThreadTree root){
ThreadNode *p11=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p11).ltag=0;
(*p11).rtag=0;
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
ThreadNode *p12=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p12).ltag=0;
(*p12).rtag=0;
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
ThreadNode *p5=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p5).ltag=0;
(*p5).rtag=0;
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
ThreadNode *p6=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p6).ltag=0;
(*p6).rtag=0;
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
ThreadNode *p7=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p7).ltag=0;
(*p7).rtag=0;
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
ThreadNode *p2=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p2).ltag=0;
(*p2).rtag=0;
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
ThreadNode *p3=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p3).ltag=0;
(*p3).rtag=0;
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
ThreadNode *p1=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p1).ltag=0;
(*p1).rtag=0;
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
//指向当前被访问的结点
ThreadNode *q=NULL;
/*
创建中序线索二叉树
*/
void createInThread(ThreadTree root){
if(root!=NULL){
inThread(root);
if((*q).rChild==NULL){
(*q).rtag=1;
}
}
}
/*
中序线索二叉树
*/
void inThread(ThreadTree root){
ThreadNode *pre=NULL;
if(root!=NULL){
inThread((*root).lChild);
pre=q;
q=root;
if((*q).lChild==NULL){
(*q).lChild=pre;
(*q).ltag=1;
}
if(pre!=NULL&&(*pre).rChild==NULL){
(*pre).rChild=q;
(*pre).rtag=1;
}
inThread((*root).rChild);
}
}
/*
找到以p为根的子树中,第一个被中序遍历的结点
*/
ThreadNode* inFirstNode(ThreadNode *p){
//循环找到最左下结点(不一定是叶子结点)
while((*p).ltag==0){
p=(*p).lChild;
}
return p;
}
/*
中序线索二叉树中找到结点p的后继结点
*/
ThreadNode* inNextNode(ThreadNode *p){
//右子树中最左下结点
if((*p).rtag==0){
return inFirstNode((*p).rChild);
}else{
return (*p).rChild;
}
}
/*
对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
*/
void inOrder(ThreadTree root){
ThreadNode *p=inFirstNode(root);
for(;p!=NULL;p=inNextNode(p)){
printf("%d ",(*p).value);
}
}
/*
找到以p为根的子树中,最后一个被中序遍历的结点
*/
ThreadNode* inLastNode(ThreadNode *p){
//循环找到最右下结点(不一定是叶子结点)
while((*p).rtag==0){
p=(*p).rChild;
}
return p;
}
/*
中序线索二叉树中找到结点p的前驱结点
*/
ThreadNode* inPreNode(ThreadNode *p){
//左子树中最右下结点
if((*p).ltag==0){
return inLastNode((*p).lChild);
}else{
return (*p).lChild;
}
}
/*
对中序线索二叉树进行逆中序遍历
*/
void revInOrder(ThreadTree root){
ThreadNode *p=inLastNode(root);
for(;p!=NULL;p=inPreNode(p)){
printf("%d ",(*p).value);
}
}
int main(){
ThreadTree root;
root=initTree(root);
root=constructTree(root);
createInThread(root);
inOrder(root);
printf("\n");
revInOrder(root);
return 0;
}
先序线索二叉树
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct ThreadNode{
//数据域
int value;
//左孩子
struct ThreadNode *lChild;
//右孩子
struct ThreadNode *rChild;
//1--前驱结点 0--左孩子
int ltag;
//1--后继结点 0--右孩子
int rtag;
};
typedef struct ThreadNode ThreadNode;
typedef struct ThreadNode* ThreadTree;
/*
初始化tree
*/
ThreadTree* initTree(ThreadTree root){
root=NULL;
return root;
}
/*
构造树
*/
ThreadTree* constructTree(ThreadTree root){
ThreadNode *p11=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p11).ltag=0;
(*p11).rtag=0;
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
ThreadNode *p12=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p12).ltag=0;
(*p12).rtag=0;
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
ThreadNode *p5=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p5).ltag=0;
(*p5).rtag=0;
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
ThreadNode *p6=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p6).ltag=0;
(*p6).rtag=0;
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
ThreadNode *p7=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p7).ltag=0;
(*p7).rtag=0;
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
ThreadNode *p2=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p2).ltag=0;
(*p2).rtag=0;
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
ThreadNode *p3=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p3).ltag=0;
(*p3).rtag=0;
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
ThreadNode *p1=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p1).ltag=0;
(*p1).rtag=0;
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
//指向当前被访问的结点
ThreadNode *q1=NULL;
/*
创建先序线索二叉树
*/
void createPreThread(ThreadTree root){
if(root!=NULL){
preThread(root);
if((*q1).rChild==NULL){
(*q1).rtag=1;
}
}
}
/*
先序线索二叉树
*/
void preThread(ThreadTree root){
ThreadNode *pre=NULL;
if(root!=NULL){
pre=q1;
q1=root;
if((*q1).lChild==NULL){
(*q1).lChild=pre;
(*q1).ltag=1;
}
if(pre!=NULL&&(*pre).rChild==NULL){
(*pre).rChild=q1;
(*pre).rtag=1;
}
if((*root).ltag==0){
preThread((*root).lChild);
}
preThread((*root).rChild);
}
}
/*
找到以p为根的子树中,第一个被先序遍历的结点
*/
ThreadNode* preFirstNode(ThreadNode *p){
if(p!=NULL){
return p;
}
return NULL;
}
/*
先序线索二叉树中找到结点p的后继结点
*/
ThreadNode* preNextNode(ThreadNode *p){
//右指针存放的是后继,直接返回
if((*p).rtag==1){
return (*p).rChild;
}else{
//右指针存放的不是后继
//判断有没有左孩子,有直接返回,否则返回右孩子
if((*p).ltag==0){
return (*p).lChild;
}else{
return (*p).rChild;
}
}
}
int main(){
return 0;
}
后序线索二叉树
//二叉树--链式存储
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
struct ThreadNode{
//数据域
int value;
//左孩子
struct ThreadNode *lChild;
//右孩子
struct ThreadNode *rChild;
//1--前驱结点 0--左孩子
int ltag;
//1--后继结点 0--右孩子
int rtag;
};
typedef struct ThreadNode ThreadNode;
typedef struct ThreadNode* ThreadTree;
/*
初始化tree
*/
ThreadTree* initTree(ThreadTree root){
root=NULL;
return root;
}
/*
构造树
*/
ThreadTree* constructTree(ThreadTree root){
ThreadNode *p11=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p11).ltag=0;
(*p11).rtag=0;
(*p11).value=11;
(*p11).lChild=NULL;
(*p11).rChild=NULL;
ThreadNode *p12=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p12).ltag=0;
(*p12).rtag=0;
(*p12).value=12;
(*p12).lChild=NULL;
(*p12).rChild=NULL;
ThreadNode *p5=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p5).ltag=0;
(*p5).rtag=0;
(*p5).value=5;
(*p5).lChild=NULL;
(*p5).rChild=p11;
ThreadNode *p6=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p6).ltag=0;
(*p6).rtag=0;
(*p6).value=6;
(*p6).lChild=p12;
(*p6).rChild=NULL;
ThreadNode *p7=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p7).ltag=0;
(*p7).rtag=0;
(*p7).value=7;
(*p7).lChild=NULL;
(*p7).rChild=NULL;
ThreadNode *p2=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p2).ltag=0;
(*p2).rtag=0;
(*p2).value=2;
(*p2).lChild=NULL;
(*p2).rChild=p5;
ThreadNode *p3=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p3).ltag=0;
(*p3).rtag=0;
(*p3).value=3;
(*p3).lChild=p6;
(*p3).rChild=p7;
ThreadNode *p1=(ThreadNode *)malloc(sizeof(ThreadNode));
(*p1).ltag=0;
(*p1).rtag=0;
(*p1).value=1;
(*p1).lChild=p2;
(*p1).rChild=p3;
root=p1;
return root;
}
//指向当前被访问的结点
ThreadNode *q3=NULL;
/*
创建后序线索二叉树
*/
void createPostThread(ThreadTree root){
if(root!=NULL){
postThread(root);
if((*q3).rChild==NULL){
(*q3).rtag=1;
}
}
}
/*
后序线索二叉树
*/
void postThread(ThreadTree root){
ThreadNode *pre=NULL;
if(root!=NULL){
postThread((*root).lChild);
postThread((*root).rChild);
pre=q3;
q3=root;
if((*q3).lChild==NULL){
(*q3).lChild=pre;
(*q3).ltag=1;
}
if(pre!=NULL&&(*pre).rChild==NULL){
(*pre).rChild=q3;
(*pre).rtag=1;
}
}
}
/*
后序线索二叉树中找到结点p的前驱结点
*/
ThreadNode* postPreNode(ThreadNode *p){
//左指针存放的是前驱,直接返回
if((*p).ltag==1){
return (*p).ltag;
}else{
//左指针存放的不是前驱
//判断有没有右孩子,有直接返回,否则返回左孩子
if((*p).rtag==0){
return (*p).rtag;
}else{
return (*p).lChild;
}
}
}
int main(){
return 0;
}
5.3.5 本节试题精选
5.4 树、森林
5.4.1 树的存储结构
树的存储方式有多种,既可采用顺序存储结构,又可采用链式存储结构,但无论采用何种存储方式,都要求能唯一地反映树中各结点之间的逻辑关系,这里介绍3种常用的存储结构。
1、双亲表示法
注意:区别树的顺序存储结构与二叉树的顺序存储结构。在树的顺序存储结构中,数组下标代表结点的编号,下标中所存的内容指示了结点之间的关系。而在二叉树的顺序存储结构中,数组下标既代表了结点的编号,又指示了二叉树中各结点之间的关系。当然,二叉树属于树,因此二叉树都可以用树的存储结构来存储,但树却不都能用二叉树的存储结构来存储。
2、孩子表示法
与双亲表示法相反,孩子表示法寻找孩子的操作非常简单,而寻找双亲的操作则需要遍历n个结点中孩子链表指针域所指向的n个孩子链表。
3、孩子兄弟表示法
孩子兄弟表示法又称二叉树表示法,即以二叉链作为树的存储结构。孩子兄弟表示法比较灵活,其最大的优点是可以方便地实现树转换为二叉树的操作,易于查找结点的孩子等,但缺点是从当前结点查找其双亲结点比较麻烦。若为每个结点增设一个parent域指向其父结点,则查找结点的父结点也很方便。
5.4.2 树、森林与二叉树的转换
5.4.3 树、森林的遍历
5.4.4 本节试题精选
7、8、9作为结论,8是根据7做出来的
5.5 树与二叉树的应用
5.5.1 哈夫曼树和哈夫曼编码
1、哈夫曼树的定义
2、哈夫曼树的构造
从上述构造过程中可以看出哈夫曼树具有如下特点:
1)每个初始结点最终都成为叶结点,且权值越小的结点到根结点的路径长度越大。
2)构造过程中共新建了n-1个结点(双分支结点),因此哈夫曼树的结点总数为2n-1。
3)每次构造都选择2棵树作为新结点的孩子,因此哈夫曼树中不存在度为1的结点。
3、哈夫曼编码
5.5.2 并查集
介绍
优化
5.5.3 本节试题精选