【考研】常考的二叉树相关算法总结(详细全面)

CSDN话题挑战赛第2期
参赛话题:学习笔记

前言

本文内容主要针对考研常考的二叉树相关算法,包括但不限于对普通二叉树的构造、查找、插入元素、(先序、中序、后序、层序)遍历,并含递归和非递归方式等的算法、二叉排序树的构造、查找和插入等等算法。

因为考研中用二叉链表的存储结构较多,所以二叉树用的存储结构都是二叉链表。

如果有错误的地方,请留言一起探讨。

(后期会继续补充有关二叉树的算法)

本文的PDF版已出:

考研常考的二叉树相关算法总结(详细全面,PDF版)-其它文档类资源-CSDN文库

可搭配以下链接一起学习:

【考研】数据结构——线索二叉树_住在阳光的心里的博客-CSDN博客

【考研】《数据结构》知识点总结.pdf_其它文档类资源-CSDN下载

【2023考研】数据结构常考应用典型例题(含真题)_住在阳光的心里的博客

一、普通二叉树

(一)链式存储结构定义

// 二叉树的二叉链表存储表示
typedef struct BiTNode {          
    TElemType data;        
    struct BiTNode *lchild, *rchild;   //左孩子和右孩子指针      
} BiTNode, *BiTree; 

(二)先序遍历

1、非递归

//第一种
//用人工栈,先序遍历(非递归)
void preorder_nonrecursion(BiTNode *bt){
    if(bt != nullptr){
        //定义人工栈
        BiTNode *stack[MaxSize];
        int top = -1;

        stack[++top] = bt;    //栈顶指针加一,根结点入栈
        BiTNode *p;

        while(top != -1){  //判断栈不为空
            p = stack[top--];    //出栈,栈顶指针减一
            printf("%c\n", p->data);

            if(p->rchild != nullptr){   //栈是先进后出,所以一定是先遍历右孩子,再左孩子
                stack[++top] = p->rchild;
            }

            if(p->lchild != nullptr){   //遍历左孩子
                stack[++top] = p->lchild;
            }
        }
    }
}
//第二种
//采用二叉链表存储结构, Visit是对结点操作的应用函数
void PreOrderTraverse(BiTree T, Status(*Visit)(TElemType)) {
    InitStack(S); 
    BiTree p = T;

    while(p != NULL || !EmptyStack(S)){    // p 不为空或栈 S 不为空
        if (p) { 
          Visit(p->data);
          push(S, p);    //为了保留访问右子树的地址
          p = p->lchild; 
        }
        else {      
          Pop(S, p);
          p = p->rchild; 
        }
    }
}

2、递归  

//先序遍历(递归)
void preorder(BiTNode *p){
    if(p != nullptr){  //判空
        printf("%c", p->data);     //假设 TElemType data; 中 data 类型是 char  
        preorder(p->lchild);   //递归左子树
        preorder(p->rchild);   //递归右子树
    }
}

(三)中序遍历

1、非递归

//第一种
//用人工栈,中序遍历(非递归)
void inorder_nonrecursion(BiTNode *bt){
    //定义人工栈
    BiTNode *stack[MaxSize];
    int top = -1;
    BiTNode *p = bt;

    if(bt != nullptr){
        while(top != -1 || p != nullptr){  //判断栈不为空或树 p 不为空
            while(p != nullptr){
                stack[++top] = p;   //栈顶指针加一,根结点入栈
                p = p->lchild;   //遍历左子树
            }
            
            if(top != -1){  //判断栈不为空
                p = stack[top--];  //出栈,栈顶指针减一
                printf("%c\n", p->data);

                p = p->rchild;   //遍历右子树
            } 
        }
    }
}

第二种非递归算法,中序遍历二叉树的算法思想如下:

(1)初始化一个空栈S,指针 p 指向根结点。

(2)申请一个结点空间 q,用来存放栈顶弹出的元素。 

(3)当 p 非空或栈 S 非空时,循环执行以下操作:

p 非空:将 p 进栈,p 指向该结点的左孩子。

栈 S 非空:弹出栈顶元素并访问,将 p 指向该结点的右孩子。

//第二种
//非递归,中序遍历二叉树,访问结点并打印结点的数据
void Process(BiTree T){ 
    IniStack(S);  
    p = T;   
    q = (BiTNode*)malloc(sizeof(BiTNode));
    while ( p || !StackEmpty(S)){
        if (p){ 
            push(S, p);    //根指针进栈
            p = p->lchild;   //遍历左子树
        }     
        else{  //p为空
            pop(S, q);     //退栈
            printf("%c\n", p->data);  //访问根结点
            p = q->rchild;    //遍历右子树
        }
    }
}

2、递归  

//中序遍历(递归)
void preorder(BiTNode *p){
    if(p != nullptr){  //判空
        preorder(p->lchild);   //递归左子树
        printf("%c", p->data);    //假设 TElemType data; 中 data 类型是 char  
        preorder(p->rchild);   //递归右子树
    }
}

 (四)后序遍历

1、非递归

//后序遍历(非递归)
void postorder_nonrecursion(BiTNode *bt){
    if(bt != nullptr){
        //定义两个人工栈
        BiTNode *stack1[MaxSize], *stack2[MaxSize];
        int top1 = -1, top2 = -1;

        BiTNode *p;
        stack1[++top1] = bt;

        while(top1 != -1){
            p = stack1[top1--];
            stack2[++top2] = p;    //注意与先序的区别,根结点放入栈2
        
            if(p->lchild){
                stack1[++top1] = p->lchild;
            }
            
            if(p->rchild){
                stack1[++top1] = p->rchild;
            }
        }

        //此时循环结束,则会将逆后序遍历的结果都存放到了栈2中,对栈2进行输出即可得到后序遍历结果
        while(top2 != -1){
            p = stack2[top2--];
            printf("%c\n", p->data);
        }
    }
}

2、递归  

//后序遍历(递归)
void preorder(BiTNode *p){
    if(p != nullptr){  //判空
        preorder(p->lchild);   //递归左子树
        preorder(p->rchild);   //递归右子树
        printf("%c", p->data);    //假设 TElemType data; 中 data 类型是 char  
    }
}

  (五)层序遍历

//层序遍历(队列,先进先出)
void level_process(BiTNode *p){
    BiTNode *que[MaxSize];
    BiTNode *q = nullptr;
    int front = 0, rear = 0;   //定义一个循环队列

    if(p != nullptr){   //树 p 不为空
        rear = (rear + 1) % MaxSize;
        que[rear] = p;   //根结点入队(入队,队尾指针加一)

        while(front != rear){ //队列不为空
            front = (front + 1) % MaxSize;
            q = que[front];  //队头元素出队(出队,队头指针加一)
            printf("%c\n", q->data);

            if(q->lchild){   //如果左子树存在,则左子树根结点入队
                rear = (rear + 1) % MaxSize;
                que[rear] = q->lchild;
            }

            if(q->rchild){   //如果右子树存在,则右子树根结点入队
                rear = (rear + 1) % MaxSize;
                que[rear] = q->rchild;
            }
        }
    }
}

/*
循环队列知识点:
判断队空:rear == front
判断队满:(rear + 1) % MaxSize == front
*/

(六)统计二叉树中叶子结点的个数

//统计叶子结点个数
//第一种
int leaf(BiTree T){
    if(!T){
        return 0;   //树空,则叶子结点数为0
    }
    else if(!T->lchild && !T->rchild){   
        return 1;    //树T只有根结点
    }
    else{
        return (leaf(T->lchild) + leaf(T->rchild));
    }
}

//第二种
int leaf(BiTree T){
    if(T == NULL) return 0;
    else{
        return leaf(T->lchild) + leaf(T->rchild) + 1;
    }
}

 (七)计算二叉树的深度

1、算法步骤

如果是空树,递归结点,深度为0,否则执行以下操作:

(1)递归计算左子树的深度,记为 m

(2)递归计算右子树的深度,记为 n

(3)如果 m > n,则二叉树的深度为 m + 1,否则为 n + 1

2、算法代码

int Depth(BiTree T){
    if(T == NULL) return 0;
    else{
        m = Depth(T->lchild);
        n = Depth(T->rchild);

        if(m > n) return (m + 1);
        else return (n + 1);
    }
}

3、拓展: 二叉树采用二叉链表存储,设计非递归算法求二叉树的高度

(1)算法思想:

采用层次遍历的算法,设置变量 level 记录当前结点所在的层数,设置变量 last 指向当前层的最右结点,每次层次遍历出队时与 last 指针比较,若两者相等,则层数加1,并让 last 指向下一层的最右结点,直到遍历完成。level 的值即为二叉树的高度。

(2)算法代码:

//采用层次遍历的非递归方法求解二叉树的高度
int Btdepth(BiTree T){
    if(!T) return 0;     //树空,高度为0
    int front = -1, rear = -1;
    int last = 0, level = 0;    // last 指向当前层的最右结点
    BiTree Q[maxsize];   //设置队列Q,元素是二叉树结点指针且容量足够

    Q[++rear] = T;   
    BiTree p;

    while(front < rear){    //队不空,则循环
        p = Q[++front];
        if(p->lchild) Q[++rear] = p->lchild;    //左孩子入队
        if(p->rchild) Q[++rear] = p->rchild;    //右孩子入队
        if(front == last){    //处理该层的最右结点
            level++;     //层数加1
            last = rear;    // last 指向下层
        }
    }
    return level;
}

(八)删除二叉树中以某个结点为根结点的子树

1、算法思想

先序遍历找到符合条件的结点(其它遍历方法亦可),然后删除以该结点为根结点的子树。 最后把该结点的父结点的相应的指针域置为 NULL。为此,需在算法中设置一个指针变量用以指示当前结点的父结点。 

2、算法代码

#define MAX_STACK 50 

//删除二叉树中以某个结点为根结点的子树
BiTree DeleteSubtree(BiTree &T, int item) 
{ 
    BiTree STACK[MAX_STACK], q, p = T; 
    int top = -1; 
    if (T->data == item) {   //树 T 若刚好以 item 为根结点
        DestroyBT(T);    //销毁二叉树T
        T = NULL; 
        return NULL; 
    } 
    else{ 
        while (p != NULL || top != -1){   //当 p 不为空,或栈不为空
            while (p != NULL){  // p 不为空
                if (p->data == item){   
                    if (q->lchild == p) 
                        q->lchild = NULL; 
                    else 
                        q->rchild = NULL; 

                    DestroyBT(p); 
                    return T; 
                } 

                STACK[++top]= p;  //栈顶指针加一,p 进栈
                q = p; 
                p = p->lchild;    //遍历左子树
            } 
            q = STACK[top--]; 
            p = q->rchild;    //遍历右子树
        } 
    } 
}

(九)求结点所在层次

1、算法思想

采用后序遍历的非递归算法对二叉树进行遍历,遍历过程中对每一个结点判断其是否为满足条件的结点,若是满足条件的结点,则此时堆栈中保存的元素个数再加 1 即为该结点所在的层次。 

2、算法代码

#define MAX_STACK 50 

//求结点所在层次
int LayerNode(BiTree T, int item) 
{ 
     BTree STACK1[MAX_STACK], p = T; 
     int STACK2[MAX_STACK], flag, top = -1; 
     while (p != NULL || top != -1){ 
         while (p != NULL){ 
             STACK1[++top] = p; 
             STACK2[top] = 0; 
             p = p->lchild; 
         } 
         p = STACK1[top]; 
         flag = STACK2[top--]; 
         if (flag == 0){ 
             STACK1[++top] = p; 
             STACK2[top] = 1; 
             p = p->rchild; 
         } else { 
             if (p->data == item) 
                 return top+2; 
             p = NULL; 
         } 
     } 
} 

(十)判别两棵树是否相等(二叉链表)

1、算法思想先判断当前节点是否相等(需要处理为空、是否都为空、是否相等),如果当前节点不相等,直接返回两棵树不相等;如果当前节点相等,那么就递归的判断他们的左右孩子是否相等。

2、代码如下

// 比较当前根,然后比较左子树和右子树
int compareTree(BiTNode* tree1, BiTNode* tree2){
    bool tree1IsNull = (tree1 == NULL);
    bool tree2IsNull = (tree2 == NULL);

    if(tree1IsNull != tree2IsNull){
        return 1;
    }

    if(tree1IsNull && tree2IsNull){    //如果两个都是NULL,则相等
        return 0;
    }

    //如果根节点不相等,直接返回不相等,否则的话,看看他们孩子相等不相等
    if(tree1->val != tree2->val){
        return 1;
    }

    return (compareTree(tree1->left, tree2->left) && compareTree(tree1->right, tree2->right));
}

(十一)交换二叉树每个结点的左孩子和右孩子 

1、算法思想:如果某结点左右子树为空,返回,否则交换该结点左右孩子,然后递归交换左右子树。

2、代码如下

//交换二叉树每个结点的左孩子和右孩子
void ChangeLR(BiTree &T){
	BiTree temp;
	if(T->lchild == NULL && T->rchild == NULL)
		return;
	else{   //交换左右孩子
		temp = T->lchild;
		T->lchild = T->rchild;
		T->rchild = temp;
	}

	ChangeLR(T->lchild);  //递归交换左子树
	ChangeLR(T->rchild);  //递归交换右子树
}

(十二)设计二叉树的双序遍历算法

双序遍历:指对于二叉树的每一个结点来说,先访问这个结点,再按双序遍历它的左子树,然后再一次访问这个结点,接下来按双序遍历它的右子树)

1、算法思想:若树为空,返回;若某结点为叶子结点,则仅输出该结点;否则先输出该结点,递归遍历其左子树,再输出该结点,递归遍历其右子树。

2、代码如下

//设计二叉树的双序遍历算法
void DoubleTraverse(BiTree T){
	if(T == NULL)
		return;
	else if(T->lchild == NULL && T->rchild == NULL)
		cout << T->data;     //叶子结点输出
	else{
		cout << T->data;
		DoubleTraverse(T->lchild);   //递归遍历左子树
		cout << T->data; 
		DoubleTraverse(T->rchild);   //递归遍历右子树
	}
}

(十三)计算二叉树最大的宽度

1、算法思想(二叉树的最大宽度是指二叉树所有层中结点个数的最大值)。

求二叉树高度的算法见(七)。求最大宽度可采用层次遍历的方法,记下各层结点数,每层遍历完毕,若结点数大于原先最大宽度,则修改最大宽度。

2、代码如下

//求二叉树 bt 的最大宽度
int Width(BiTree bt){
    if (bt == NULL) return (0);   //空二叉树宽度为 0
    else{
        BiTree Q[];    // Q 是队列,元素为二叉树结点指针,容量足够大
        front = 1;   // front 队头指针
        rear = 1;    // rear 队尾指针
        last = 1;    // last 同层最右结点在队列中的位置

        temp = 0;     // temp 记局部宽度
        maxw = 0;     // maxw 记最大宽度
        Q[rear] = bt;      // 根结点入队列

        while(front <= last){
            p = Q[front++]; 
            temp++;   //同层元素数加1

            if (p->lchild != NULL)  
                Q[++rear] = p->lchild;   //左子女入队

            if (p->rchild != NULL)  
                Q[++rear] = p->rchild;   //右子女入队

            if (front > last){      //一层结束, 
	            last = rear;   //last指向下层最右元素, 更新当前最大宽度
                if(temp > maxw) maxw = temp;
	            temp = 0;
            }   
        }
    }
    return (maxw);
}

(十四)层次顺序遍历二叉树,统计树中具有度为 1 的结点数目

1、算法思想:若某个结点左子树空右子树非空,或者右子树空左子树非空,则该结点为度为 1 的结点。

2、代码如下

//层次遍历二叉树,并统计度为1的结点的个数
int Level(BiTree bt){
    int num = 0; // num 统计度为1的结点的个数
    if(bt){
        QueueInit(Q); 
        QueueIn(Q, bt);    // Q 是以二叉树结点指针为元素的队列
    
        while(!QueueEmpty(Q)){
            p = QueueOut(Q); 
            cout << p->data;     //出队,访问结点

            if(p->lchild && !p->rchild || !p->lchild && p->rchild)
                num++;   //度为 1 的结点加一

            if(p->lchild) 
                QueueIn(Q, p->lchild); //非空左子女入队

            if(p->rchild) 
                QueueIn(Q, p->rchild); //非空右子女入队
        } 
    }     
    return(num);     //返回度为1的结点的个数 
}

(十五)求任意二叉树中第一条最长的路径长度,并输出此路径上各结点的值

1、算法思想:因为后序遍历栈中保留当前结点的祖先的信息,用一变量保存栈的最高栈顶指针,每当退栈时,栈顶指针高于保存最高栈顶指针的值时,则将该栈倒入辅助栈中,辅助栈始终保存最长路径长度上的结点,直至后序遍历完毕,则辅助栈中内容即为所求。

2、代码如下

//求二叉树中的第一条最长路径长度
void LongestPath(BiTree bt){
    BiTree p = bt, l[], s[];   //l, s是栈,元素是二叉树结点指针,l中保留当前最长路径中的结点

    int i,top = 0, tag[], longest = 0;

    while(p || top > 0){
        while(p){   //沿左分枝向下
            s[++top] = p;
            tag[top] = 0; 
            p = p->Lc;
        } 
        if(tag[top] == 1){    //当前结点的右分枝已遍历
            if(!s[top]->Lc && !s[top]->Rc){  //只有到叶子结点时,才查看路径长度
                if(top > longest){   //保留当前最长路径到 l 栈,记住最高栈顶指针,退栈
                    for(i = 1; i <= top; i++){ 
                        l[i] = s[i]; 
                        longest = top; 
                        top--;
                    }
                }
            }
        }
        else if(top > 0){ //沿右子分枝向下
            tag[top] = 1; 
            p = s[top].Rc;
        }   
    }
}

(十六) 输出二叉树中从每个叶子结点到根结点的路径

1、算法思想:采用先序遍历的递归方法,当找到叶子结点 *b 时,由于 *b 叶子结点尚未添加到path 中,因此在输出路径时还需输出 b->data 值。

2、代码如下

//输出二叉树中从每个叶子结点到根结点的路径
void AllPath(BTNode *b, ElemType path[], int pathlen){  //设 pathlen 初始值为1
    int i;
    if (b != NULL){
        if (b->lchild == NULL && b->rchild == NULL){   //*b为叶子结点
            cout << " " << b->data << "到根结点路径:" << b->data << " ";
            for (i = pathlen - 1; i > 0; i--)
                cout << path[i] << " ";
            cout << endl;
        }
        else{
            path[pathlen] = b->data;           //将当前结点放入路径中
            pathlen++;                     //路径长度增1

            AllPath(b->lchild, path, pathlen);   //递归扫描左子树
            AllPath(b->rchild, path, pathlen);   //递归扫描右子树
            pathlen--;                     //恢复环境
         }
     }
}

备注:根据评论区留言,才发现这个函数写错了,现已修改,得到的测试结果如下: 

 (要注意:此博文的pdf版(在上方链接中)未修改这一点,请大家自行修改。)

(十七)求先序遍历序列中第 k 个结点的值(1 <= k <= n,二叉链表) 

1、算法思想

设置一个全局变量 i 记录已访问过的结点的序号,其初值是根结点在先序序列中的序号,即 1。

当二叉树 b 为空时,返回特殊字符 ‘#’ ,

当 i == k 时,表示找到了满足条件的结点,返回 b->data;

当 i 不等于 k 时,递归地在左子树中查找,若找到则返回该值,否则继续递归地在右子树中查找,并返回其结果。(n 是二叉树中结点个数

2、代码如下

int i = 1;  //遍历序号的全局变量
ElemType PreNode(BiTree b, int k){
    if(b == NULL) return '#';   //空结点,返回特殊字符

    if(i == k) return b->data;   //相等,则当前结点即为第 k 个结点

    i++;
    ch = PreNode(b->lchild, k);   //左子树递归查找
    if(ch != '#') return ch;   //若在左子树中,则返回该值
    ch = PreNode(b->rchild, k);   //右子树递归查找
    return ch;
}

(十八)判别给定二叉树是否是完全二叉树(二叉链表)

 1、算法思想

根据完全二叉树的定义,具有 n 个结点的完全二叉树与满二叉树中编号从 1 至 n 的结点一一对应。通过采用层次遍历的算法,将所有结点加入队列(包括空结点)。遇到空结点时,查看其后是否有非空结点。若有,则二叉树不是完全二叉树。

2、代码如下

bool IsComplete(BiTree T){
    InitQueue(Q);
    if(!T) return 1;    //空树为满二叉树

    EnQueue(Q, T);
    while(!IsEmpty(Q)){
        DeQueu(Q, p);    
        if(p){    //结点非空,将其左、右子树入队列
            EnQueue(Q, p->lchild);
            EnQueue(Q, p->rchild);
        }
        else{   //结点为空,检查其后是否有非空结点
            while(!IsEmpty(Q)){
                DeQueu(Q, p);
                if(p) return 0;    //结点非空,则二叉树为非完全二叉树
            }
        }
    }
    return 1;
}

二、二叉排序树

(一)结点定义

typedef struct BTNode{
    int key;
    struct BTNode *lchild, *rchild;
}BTNode;

(二)插入算法

//插入算法,往二叉排序树中插入元素e
int BSTInsert(BTNode *&bt, int e){ //因为 bt 要改变,所以用引用型指针
    if(bt == nullptr){
        bt = (BTNode*)malloc(sizeof(BSTNode));    //创建新的结点
        bt->lchild - bt->rchild = nullptr;   //将左指针和右指针置空
        bt->key = e; 
        return 1;
    }
    else{
        if(bt->key == e){
            return 0;    //关键字已存在于树中
        }
        else if(bt->key > e){
            return BSTInsert(bt->lchild, e);
        }
        else{
            return BSTInsert(bt->rchild, e);
        }
    }
}

【补充】

二叉树是一种动态查找表。特点是,树的结构不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。

所以,遇到重复的关键字值,不用插入。

(三)查找算法

// 递归方式,查找二叉排序树中是否有元素e 
BTNode* BSTSearch(BTNode *bt, int e){ 
    if(bt == nullptr)
        return nullptr;

    if(bt->key == e){
        return bt;
    }
    else if(key < bt->key){
        return BSTSearch(bt->lchild, e);
    }
    else{
        return BSTSearch(bt->rchild, e);
    }
}

(四)构造算法

void CreateBST(BTNode *&bt, int key[], int n){
    bt = nullptr;   //将树清空
    for(int i = 0; i < n; i++){
        BSTInsert(bt, key[i]);
    }
}

(五)从大到小输出二叉排序树中所有的值不小于 x 的关键字

typedef struct BitTree{
    int data;
    struct BitTree *lchild, *rchild;
};

void printBt(BitTree &p){
    if(!p) return;
    if(p->data < x) printBt(p->rchild);
    else{
        printBt(p->rchild);           //遍历右子树
        printf("%d ", p->data);         //打印关键字
        printBt(p->lchild);           //遍历左子树
    }
}

 (六)在 VS 上运行示例

#include <stdio.h>
#include <stdlib.h>

int x = 35;

typedef struct BitTree {
    int data;
    struct BitTree* lchild, * rchild;
}BitTree, * BT;

//插入算法,往二叉排序树中插入元素e  (50,72,43,85,75,20,35,45,65,30)
void BSTInsert(BT &bt, int e) {
    if (bt == NULL) {
        bt = (BitTree*)malloc(sizeof(BitTree));    //创建新的结点
        bt->lchild = bt->rchild = NULL;   //将左指针和右指针置空
        bt->data = e;
    }
    else {
        if (bt->data > e) {
            return BSTInsert(bt->lchild, e);
        }
        else {
            return BSTInsert(bt->rchild, e);
        }
    }
}

void CreateBST(BT &bt, int key[], int n) {
    bt = NULL;   //将树清空
    for (int i = 0; i < n; i++) {
        BSTInsert(bt, key[i]);
    }
}

void printBt(BT &p) {
    if (!p) return;
    if (p->data < x) printBt(p->rchild);
    else {
        printBt(p->rchild);           //遍历右子树
        printf("%d ", p->data);         //打印关键字
        printBt(p->lchild);           //遍历左子树
    }
}

//中序遍历(递归)
void midorder(BT p) {
    if (p != NULL) {  //判空
        midorder(p->lchild);   //递归左子树
        printf("%d ", p->data);    //假设 TElemType data; 中 data 类型是 char  
        midorder(p->rchild);   //递归右子树
    }
}

//先序遍历(递归)
void preorder(BT p) {
    if (p != NULL) {  //判空
        printf("%d ", p->data);     //假设 TElemType data; 中 data 类型是 char  
        preorder(p->lchild);   //递归左子树
        preorder(p->rchild);   //递归右子树
    }
}

int main(void) {
    int key[10] = { 50,72,43,85,75,20,35,45,65,30 };
    int n = 10;
    BitTree* bt = (BitTree*)malloc(sizeof(BitTree));
    CreateBST(bt, key, n);
    printf("先序遍历:\n");
    preorder(bt);
    printf("\n\n中序遍历:\n");
    midorder(bt);
    printf("\n\n比35大的:\n\n");
    printBt(bt);
    return 0;
}

运行结果图: 

三、孩子兄弟法

孩子兄弟法,又称二叉树表示法、二叉链表表示法,即以二叉链表做树的存储结构。

链表中的结点的两个链域,分别指向该结点的第一个孩子结点和下一个兄弟结点。

孩子兄弟表示法的结点
firstchilddatanextsibling
//树的二叉链表(孩子-兄弟)存储表示
typedef struct CSNode{
    ElemType data;
    struct CSNode *firstchild, *nextsibling;
}CSNode, *CSTree;

【注意】

利用此种结构,便于实现各种树的操作,是应用较为普遍的一种树的存储表示方法。

首先,易于实现找结点孩子等的操作。例如,若要访问结点 x 的第 i 个孩子,则只要先从 firstchild 域找到第 1 个孩子结点,再沿着孩子结点的 nextsibling 域连续走 i - 1 步,便可找到 x 的第 i 个孩子。

若为每个结点增设一个 parent 域,则同样能方便地实现查找双亲的操作。

(一)求以孩子兄弟表示法存储的森林的叶子结点数

1、算法思想:

当森林(树)以孩子兄弟表示法存储时,若结点没有孩子(fch == null),则它必是叶子,总的叶子结点个数是孩子子树(fch)上的叶子树和兄弟子树(nsib)上叶结点个数之和。

2、算法代码:

typedef struct node{
    ElemType data;   //数据域
    struct node *fch, *nsib;   //孩子与兄弟域
}*Tree;

int Leaves(Tree t){
    if(t == NULL) return 0;   //树空,返回0
    if(t->fch == NULL)     //若结点无孩子,则该结点必是叶子
        return 1 + leaves(t->nsib);   //返回叶子结点和其兄弟子树中的叶子结点数
    else   //孩子子树和兄弟子树中叶子数之和
        return Leaves(t->fch) + Leaves(t->nsib);
}

(二)以孩子兄弟表示法为存储结构,用递归算法求树的深度

1、算法思想:

采用递归算法,若树为空,高度为零;否则,高度为第一子女树高度加 1 和兄弟子树高度的大者。其非递归算法使用队列,逐层遍历树,取得树的高度。

2、算法代码:

int Height(CSTree bt){
    int hc, hs;
    if(bt == NULL) return 0;
    else{    //否则,高度取子女高度加1或兄弟子树高度的大者
        hc = height(bt->firstchild);   //第一子女树高
        hs = height(bt->nestsibling);   //兄弟树高

        if(hc + 1 > hs) 
            return hc+1;
        else 
            return hs;
    }
}

(三)构造树的孩子-兄弟链表(已知树的层次序列和每个结点的度)

1、算法思想:

本题与树的层次序列有关,可设立一个辅助数组 pointer[] 存储新建树的各结点的地址,再根据层次序列与每个结点的度,逐个链接结点。

2、算法代码:

#define maxNode 15
//层次序列e[]与各结点的度degree[]
void createCSTree_Degree(CSTree &T, DataType e[], int degree[], int n){
    CSNode *pointer = new CSNode[maxNodes];   //判断 pointer[i] 为空的语句未写
    int i, j, d, k = 0;
    for(i = 0; i < n; i++){   //初始化
        pointer[i]->data = e[i];
        pointer[i]->lchild = pointer[i]->rsibling = NULL;
    }
    for(i = 0; i < n; i++){
        d = degree[i];    //结点 i 的度数
        if(d){
            k++;  //k为子女结点序号
            pointer[i]->lchild = pointer[k];    //建立 i 与子女 k 间的链接
            for(j = 2; j <= d; j++){
                k++;
                pointer[k-1]->rsibling = pointer[k];
            }
        }
    } 
    T = pointer[0];
    delete []pointer;
}

【待补充内容,等一等博主把后面的试卷做完,添加相关例题】

  • 12
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

住在阳光的心里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值