树
存储结构定义
typedef struct Node{
DataType data;
struct Node *lchild, *rchild;
int ltag = 0, rtag = 0;
}BitNode, *BiTree;
树的遍历
// 基操1 先序 中序 后序
void Traverse(BiTree root){
if (root != NULL){
// visit(); 先序 访问根节点
Traverse(root->lchild); // 递归遍历左子树
// visit(); 中序 访问根节点
Traverse(root->rchild); // 递归遍历右子树
// visit(); 后序 访问根节点
}
}
先序 - 统计叶子数目(遍历)
// 应用 统计叶子结点数目/打印输出
int count = 0;
void leaf(BiTree root){
if(root != NULL){
leaf(root->lchild);
// 访问时统计叶子结点操作
if(root->lchild == NULL && root->rchild == NULL){
count ++;
printf("%d ", root->data);
}
leaf(root->rchild);
}
}
先序- 建立链式二叉树(malloc创建结点)
// 基操2 建立一颗链式二叉树
void createBiTree(BiTree *T){
char ch = getchar();
if(ch = '#') *T = NULL;
else {
*T = (BiTree)malloc(sizeof(BitNode));
(*T)->data = ch;
createBiTree(&(*T)->lchild);
createBiTree(&(*T)->rchild);
}
}
先序- 统计二叉树高度(代参遍历)
// 法二 前序遍历 求二叉树高度 需要全局变量存储深度
int depth = 0;
void preTreeDepth(BiTree T, int h)
{
if(T != NULL){
if(h > depth) depth = h;
preTreeDepth(T->lchild, h + 1); // 遍历左子树
preTreeDepth(T->rchild, h + 1); // 遍历右子树
}
}
中序-非递归解法(模拟栈操作)
// 基操4 中序遍历非递归算法
// 从根节点出发以此遍历左子树 非空的进栈,为空的话 弹出栈顶 遍历右子树
void inOrder(BiTree root){
InitStack(&S);
BiTree p = root;
while(p != NULL || !isEmpty(S)){
if(p!=NULL){
push(&S, p);
p = p->lchild;
}
else{
pop(&S, &p);
printf("%d", p->data);
p = p->rchild;
}
}
}
中序-二叉线索树(pre指针保留)
// 基操5 以中序遍历为例建立一颗线索二叉树
BiTree pre = NULL;
void Inthread(BiTree root){
if(root != NULL){
Inthread(root->lchild);
// 对结点进行visit()函数 利用空链域
if(root->lchild == NULL) root->lchild == pre, root->ltag = 1; // 前继线索
if(root->rchild == NULL && pre != NULL) pre->rchild = root,root->rtag = 1; // 后继线索
pre = root;
Inthread(root->rchild);
}
}
二叉线索树前驱后继查找
// 基操6 中序线索树 找前继结点
BitNode* findPre(BitNode *p){
BitNode *pre;
// 如果ltag有线索,直接访问
if(p->ltag == 1) pre = p->lchild;
else{
// 在p的左子树找最右下结点
BitNode *q;
for(q = p->lchild; q!=NULL; q = q->rchild);
pre = q;
}
return pre;
}
// 基操7 中序线索树 找后继结点
BitNode* findNext(BitNode *p){
BitNode *next;
// 如果 rtag有线索,直接访问
if(p->rtag == 1) next = p->rchild;
else{
// 在p的右子树中找最左下的结点
BitNode *q;
for(q = p->rchild; q != NULL; q = q->lchild);
next = q;
}
return next;
}
// ps: 对于先序的线索遍历树:后继就是左孩子结点,如果没有左孩子就是右孩子,没有右孩子就是rchild 指针域
// ps2:对于先序的,前继要先看是不是根,是的话就是NULL,再看是不是双亲结点的左孩子,或者右孩子(左孩子为空)那双亲就是,如果左孩子不为空,那就是左孩子
中序-遍历二叉线索树(初始结点->rchild)
// 基操8 遍历中序二叉线索树
// 首先确定初始结点
BitNode* findFirst(BiTree T){
if(T == NULL) return NULL;
BitNode *p = T;
while(p != NULL) p = p->lchild;
return p;
}
// 从第一个结点开始 一直顺着rchild找
void TinOrder(BiTree T){
BitNode *p = findFirst(T);
while (p){
printf("%d ", p->data);
p = findNext(p);
}
}
逆中序- 横向树形二叉树(代参遍历)
// 基操3 横向树形二叉树 逆中序实操
void printTree(BiTree T, int Layer){
if(T == NULL) return ;
printTree(T->rchild, Layer + 1);
// 根据当前所在层数打印
for(int i = 0; i < Layer; i++ ) printf(" ");
printf("%c\n", T->data);
printTree(T->lchild, Layer + 1);
}
后序 - 统计叶子数目(递推)
// 法二 后续遍历递归 统计个数
int leaf2(BiTree root){
int leafCount = 0;
if(root == NULL) leafCount = 0;
else if(root->lchild == NULL && root->rchild == NULL) leafCount = 1;
else{
// 叶子个数 = 左子树叶子数 + 右子树叶子数
leafCount = leaf2(root->lchild) + leaf2(root->rchild);
return leafCount;
}
}
后序- 统计二叉树高度(递推)
// 基操3 后续遍历计算二叉树高度经典程序
// 核心递归式 H = max{H(L),H(R)} + 1
int getTreeHeight(BiTree T){
int hl, hr; // 分别表示左子树高度 右子树高度
if(T != NULL){
hl = getTreeHeight(T->lchild);
hr = getTreeHeight(T->rchild);
return (hl > hr ? hl : hr) + 1; // 非空树是左子树深度和右子树较大者 + 1
}
else return 0; // 空树 高度为0
}
后序-判断两颗树是否相似(同时遍历)
// 题1:判断两棵树是否为相似(形状相同)
// 后续遍历的一个应用 实际上我可以看空链域的对应父节点是否相同
bool isLike(BiTree T1, BiTree T2){
if(T1 == NULL && T2 == NULL) return true; // 只有两者都为空时,形状才相同
else if(T1 ==NULL || T2==NULL) return false; // 两者有一个为空,那么形状一定不同
else{
// 两个都是非空结点,继续往左右子树递归
int test1 = isLike(T1->lchild, T2->lchild); // 左子树是否相同
int test2 = isLike(T2->lchild, T2->rchild); // 右子树是否相同
return (test1 && test2);
}
}
层序遍历(队列)
// 题2 树的层次遍历
int LayerTraverse(BiTree T){
SeqQueue Q;
BiTree p;
InitQueue(&Q);
if(T == NULL) return Error;
EnQueue(&Q, T); // 插入T的根节点
while(Q.front != Q.rear){
DeQueue(&Q, &p);
printf("%d ", p->data);
if(p->lchild) EnQueue(&Q, p->lchild);
if(p->rchild) EnQueue(&Q, p->rchild);
}
return OK;
}
附录:队列和栈基操
栈
typedef struct
{
StackElemType elem[Stack_Size]; /* elem是存放栈中的一维数组 */
int top; /* 栈顶元素的下标,空栈为-1 */
}SeqStack;
SeqStack S;
// 基操 1 初始化
void InitStack(SeqStack *S){
S->top = -1;
}
// 基操2 入栈 注意判满 , 我用的是栈顶指针指向元素的版本
int push(SeqStack *S, StackElemType x)
{
if(S->top == Stack_Size) return Error; // 栈满
S->top ++ ;
S->elem[S->top] = x ;
return OK;
}
// 基操3 出栈 注意判空
int pop(SeqStack *S, StackElemType *x){
if(S->top == -1) return Error;
*x = S->elem[S->top];
S->top--;
return OK;
}
// 基操4 取栈顶元素 注意判空
int getTop(SeqStack S, StackElemType *x){
if(S.top == -1) return Error;
*x = S.elem[S.top];
return OK;
}
bool isEmpty(SeqStack S){
return S.top == -1;
}
队列
typedef struct{
QueueElemType elem[MAXSIZE];
int front; // 指向表头
int rear; // 指向表尾
}SeqQueue;
// 循环队列基操1 初始化
void InitQueue(SeqQueue *Q){
Q->front = Q->rear = 0;
}
// 循环队列基操2 入队 注意队满的情况
int EnQueue(SeqQueue *Q, QueueElemType x){
if((Q->rear + 1) % MAXSIZE == Q->front) return Error;
Q->elem[Q->rear] = x;
Q->rear = (Q->rear + 1) % MAXSIZE;
return OK;
}
// 循环队列基操3 出队 注意队空的情况
int DeQueue(SeqQueue *Q, QueueElemType *x){
if(Q->front == Q->rear) return Error;
*x = Q->elem[Q->front]; // 队首元素出队
Q->front = (Q->front + 1) % MAXSIZE;
return OK;
}
// 队列基操 判空
int isEmpty(SeqQueue Q){
return Q->front == Q->rear;
}