王道——数据结构——树与二叉树(3)

系列文章目录

其他章节相关文章

王道——数据结构——栈和队列(1)

本章节其他相关文章

王道——数据结构——树与二叉树(1)
王道——数据结构——树与二叉树(2)
王道——数据结构——树与二叉树(4)


文章目录

  • 系列文章目录
    • 其他章节相关文章
    • 本章节其他相关文章
  • 前言
  • 一、5.3节


前言

本文为王道数据结构的第五章——树与二叉树的编程题。
运行软件:vscode
使用c++文件编写
本文所使用的树为《王道——数据结构——树与二叉树(1)》中建立的树


一、5.3节

11、已知二叉树以二叉链表存储,编写算法完成:对于树中的每个元素值为x的结点,删去以它为根节点的子树,并释放相应的空间。

// 第十一题 使用层次遍历主树,发现结点值为x时,对x的子树进行后续遍历,访问各结点时删除结点
// 复杂版
void test11(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    PreOrder(T);
    int x;
    printf("\n请输入你想删除的元素值:");
    scanf("%d", &x);
    BiTNode *p, *temp, *last, *par;
    LinkQueue Q;  // 使用队列进行层次遍历,只有结点不为x的值才能入队
    InitLinkQueue(Q);
    LinkStack S;
    InitLinkStack(S); // 使用栈进行后续遍历
    EnQueue(Q, T);
    while(Q.front->next != NULL){
        DeQueue(Q, p);
        temp = p->lchild;
        if(temp && temp->data.value == x){  // 删去以x为根节点的子树
            while (temp || S != NULL){
                if(temp){
                    Push(S, temp);
                    temp = temp->lchild;
                }
                else{
                    if(S->data->rchild != last && S->data->rchild != NULL)
                        temp = S->data->rchild;
                    else{
                        Pop(S, last);
                        free(last);
                    }
                }
            }
            p->lchild = NULL;
        }
        else if(temp != NULL)
            EnQueue(Q, p->lchild);
        temp = p->rchild;
        if(temp && temp->data.value == x){  // 删去以x为根节点的子树
            while (temp || S != NULL){
                if(temp){
                    Push(S, temp);
                    temp = temp->lchild;
                }
                else{
                    if(S->data->rchild != last && S->data->rchild != NULL)
                        temp = S->data->rchild;
                    else{
                        Pop(S, last);
                        free(last);
                    }
                }
            }
            p->rchild = NULL;
        }
        else if(p->rchild != NULL)
            EnQueue(Q, p->rchild);
    }
    PreOrder(T);
}

// 简洁版 通过函数调用实现
void Delechild(BiTree T){
    if(T->lchild != NULL)
        Delechild(T->lchild);
    if(T->rchild != NULL)
        Delechild(T->rchild);
    free(T);
}

void test11_2(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    PreOrder(T);
    int x;
    printf("\n请输入你想删除的元素值:");
    scanf("%d", &x);
    BiTNode *p;
    LinkQueue Q;  // 使用队列进行层次遍历,只有结点不为x的值才能入队
    InitLinkQueue(Q);
    EnQueue(Q, T);
    while(Q.front->next != NULL){
        DeQueue(Q, p);
        if(p->lchild != NULL){
            if(p->lchild->data.value == x){
                Delechild(p->lchild);
                p->lchild = NULL;
            }
            else
                EnQueue(Q, p->lchild);
        }
        if(p->rchild != NULL){
            if(p->rchild->data.value == x){
                Delechild(p->rchild);
                p->rchild = NULL;
            }
            else
                EnQueue(Q, p->rchild);
        }
    }
    PreOrder(T);
}

12、在二叉树中查找值为x的结点,试编写算法(用C语言)打印值为x的结点的所有祖先节点,假设值为x的结点不多于一个

// 第十二题(用C) 使用后续遍历,设立标志tag=0,当发现x时tag变为1,访问节点时判断tag是否为1,如果为1打印输出该结点
int test12(BiTree T, int x, int tag){
    if(T->data.value == x)
        tag = 1;
    if(tag == 0 && T->lchild != NULL)
        tag = test12(T->lchild, x, tag);
    if(tag == 0 && T->rchild != NULL)
        tag = test12(T->rchild, x, tag);
    if(tag == 1 && T->data.value != x)
        printf("%d ", T->data.value);
    return tag;

    // 运行代码
    // BiTree T;
    // InitBiTree(T);
    // buildTree(T);
    // int x, tag = 0;
    // printf("请输入你想查找的数字:");
    // scanf("%d", &x);
    // x = test12(T, x, tag);
}

13、设一棵二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根节点的指针,p和q分别为指向二叉树中任意两个结点的指针,试编写算法ANCESTOR(ROOT, p, q, r),找到p和q的最近公共结点r。

// 第十三题 使用后续遍历的方法建立两个栈分别保存p,q的祖先结点,假设p在q左边,则必先遍历到p再遍历到q
// 使用新的栈对两个栈进行逆置,分别对两个新的栈进行遍历找到最近公共结点
// 找a结点所有的祖先节点并保存在栈中
LinkStack Stackparent(BiTree T, int a){
    LinkStack S;
    InitLinkStack(S);
    BiTNode *p = T, *last;
    while (p || S!= NULL)
    {
        
        if(p){
            Push(S, p);
            p = p->lchild;
        }
        else{
            if(S->data->rchild != NULL && S->data->rchild != last)
                p = S->data->rchild;
            else{
                if(S->data->data.value == a)
                    break;
                Pop(S, last);
            }
        }
    }
    return S;
}

void test13(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    printf("请输入你想查找的第一个结点值:");
    int a, b;
    scanf("%d", &a);
    printf("请输入你想查找的第二个结点值:");
    scanf("%d", &b);
    LinkStack S1, S2, SN1, SN2;
    InitLinkStack(S1);
    InitLinkStack(S2);
    InitLinkStack(SN1);
    InitLinkStack(SN2);
    BiTNode *temp;
    S1 = Stackparent(T, a);  // 找a的所有祖先节点
    S2 = Stackparent(T, b);  // 找b的所有祖先结点
    while(S1 != NULL){  // 将S1逆置
        Pop(S1, temp);
        Push(SN1, temp);
    }
    while(S2 != NULL){  // 将S2逆置
        Pop(S2, temp);
        Push(SN2, temp);
    }
    StackNode *p = SN1, *q = SN2;
    while(p->next != NULL && q->next != NULL){  // 如果遍历的其中一个栈的最后一个元素还没有找到不同的元素,那么其中一个结点是另一个结点的孩子结点
        if(p->next->data == q->next->data){  // 从一棵树找到的两个结点,根节点肯定是公共结点,这里直接从第二个结点开始计算 
            p = p->next;
            q = q->next;
        }
        else
            break;
    }
    printf("最近公共结点值为:%d",p->data->data.value);
}

14、假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度。


// 第十四题 使用队列进行层次遍历,用weigh计算每层结点个数,weighest保存最宽的层结点数
// 使用last指针指向每层的最后一个元素,当访问到last指针时,last指针指向下一层最后一个元素
void test14(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    LinkQueue Q;
    InitLinkQueue(Q);
    BiTNode *p = T, *last = T;
    int weight = 0, weightest = 0;
    EnQueue(Q, p);
    while(Q.front->next != NULL){
        DeQueue(Q, p);
        weight++;
        if(p->lchild != NULL)
            EnQueue(Q, p->lchild);
        if(p->rchild != NULL)
            EnQueue(Q, p->rchild);
        if(p == last){
            last = Q.rear->data;
            if(weight > weightest)
                weightest = weight;
            weight = 0;
        }
    }
    printf("树的宽度:%d", weightest);
}

15、设有一棵满二叉树(所有结点值均不同),已知其先序序列为pre,设计一个算法求其后序序列post。

// 第十五题 将先序序列的第一个元素放到后序序列的最后一个元素上,先序序列除了第一个元素剩下的所有元素为左右子树的总和——根左右
// 左子树为其二分之一,将左子树从后序序列第一个位置开始防止,然后放右子树
// l1为先序序列的开始位置,h1为先序序列的最后一个元素位置,l2为后序序列开始位置,h2为后序序列最后一个元素位置
void GetPostOrder(int pre[], int l1, int h1, int post[7], int l2, int h2){
    if(l1<=h1){
        post[h2] = pre[l1];
        int half;
        half = (h1-l1)/2;
        GetPostOrder(pre, l1+1, l1+half, post, l2, l2+half-1);
        GetPostOrder(pre, l1+half+1, h1, post, l2+half, h2-1); 
    }
}

void test15(){
    int pre[7] = {1,2,4,5,3,6,7};
    int post[7];
    GetPostOrder(pre, 0, 6, post, 0, 6);
    printf("后序序列:");
    for (int i = 0; i<7; i++){
        printf("%d ", post[i]);
    }
}

16、设计一个算法将二叉树的叶节点按从左到右的顺序连成一个单链表,表头指针为head。二叉树按二叉链表方式存储,链接时用叶节点的右指针域来存放单链表指针。

// 第十六题  使用栈对二叉树进行后序遍历,找到叶子结点,将pre指针指向叶子结点,找到下一个叶子结点时,将pre的右孩子指向该结点,并将pre指向该结点
void test16(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    LinkStack S;
    InitLinkStack(S);
    BiTNode *p = T, *pre = NULL, *last, *temp, *testnode;
    while(p || S!=NULL){
        if(p){
            Push(S, p);
            p =p->lchild;
        }
        else{
            if(S->data->rchild != NULL && S->data->rchild != last)
                p = S->data->rchild;
            else{
                Pop(S, temp);
                if(temp->lchild == NULL && temp->rchild == NULL){
                    if(pre)
                        pre->rchild = temp;
                    pre = temp;
                }
                last = temp;
            }
        }
    }
    p = T;
    while(p->lchild != NULL )  // 为了方便测试,树做左边必须有一个叶子节点
            p = p->lchild;
    while(p != NULL){
        printf("%d ", p->data.value);
        p = p->rchild;
    }
    
}

17、试设计判断两棵树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;或T1的左子树和T2的左子树相似,且T1的右子树和T2的右子树相似。

// 第十七题 相似:树的结构相同,形状相同,元素可以不同,使用标志位tag记录结构是否相同,1代表相同,0代表不同
void same(BiTree T1, BiTree T2, int &tag){
    if(tag == 0)
        return;
    if(T1 == NULL && T2 != NULL){
        tag = 0;
    }
    else if(T1 != NULL && T2 == NULL){
        tag = 0;
    }
    else if(T1 != NULL){
        same(T1->lchild, T2->lchild, tag);
        same(T1->rchild, T2->rchild, tag);
    }
    return;
}

void test17(){
    BiTree T1, T2;
    printf("下面建立第一棵树\n");
    InitBiTree(T1);
    buildTree(T1);
    printf("下面建立第二棵树\n");
    InitBiTree(T2);
    buildTree(T2);
    int tag = 1;
    same(T1, T2, tag);
    if(tag == 1)
        printf("两棵树相似");
    else    
        printf("两棵树不相似");
}

18、写出在中序线索二叉树里查找指定结点在后序的前驱结点算法。

// 第十八题 找后序线索二叉树指定节点的前驱结点,如果该结点的有右孩子不为空,则前驱结点为右孩子
// 如果该结点的右孩子为空但左孩子不为空,则前驱结点为左孩子
// 如果该结点的左右孩子都为空,则前驱结点为该结点祖先节点中最近的且不被访问过的左孩子
void test18_1(ThreadTree T, ThreadNode *p){
    if(p->rtag == 0)
        printf("%d", p->rchild->data.value);
    else if(p->ltag == 0)
        printf("%d", p->lchild->data.value);
    else{
        while(p->lchild != NULL && p->ltag != 0)
            p = p->lchild;
        if(p->lchild != NULL)
            printf("%d", p->lchild->data.value);
        else    
            printf("该结点为后序遍历的第一个结点,没有前驱结点");
    }
}


void test18(){
    ThreadTree T;
    InitThreadTree(T);
    buildTree(T);
    CreatTheadThree(T);
    Inorder(T);
    int x;
    printf("\n请输入你想要查询的数:");
    scanf("%d", &x);
    ThreadNode *p = T, *first;
    while(p->ltag == 0)
        p = p->lchild;
    while(p->data.value != x){
        if(p->rtag != 0)
            p = p->rchild;
        else{
            first = p->rchild;
            while(first->ltag == 0)
                first = first->lchild;
            p = first;
        }
    }
    printf("%d\n", p->data.value);
    test18_1(T, p);
}

19、二叉树的带权路径长度(WPL)是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树T,采用二叉链表存储,结点结构为:(left, weight, right),其中叶结点的weight域保存该结点的非负权值。设root为指向T的根结点的指针,请设计求T的WPL的算法。要求:

(1)给出算法的基本设计思想;

(2)使用C或C++语言,给出二叉树结点的数据类型定义;

(3)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。

// 第十九题 采用队列实现二叉树的层次遍历,使用last标记每层最后一个元素,high记录层数,每个结点的带权路径长度为high*weight
// 树的带权路径长度为所有结点带权路径长度之和
typedef struct WPLNode{
    int weight;
    struct WPLNode *left, *right;
}WPLNode, *WPLTree;

void test19(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    LinkQueue Q;
    InitLinkQueue(Q);
    BiTNode *p, *last = T;
    int high = 1, sum = 0;
    EnQueue(Q, T);
    while(Q.front->next != NULL){
        DeQueue(Q, p);
        sum += high*p->data.value;
        if(p->lchild)
            EnQueue(Q, p->lchild);
        if(p->rchild)
            EnQueue(Q, p->rchild);
        if(p == last){
            high++;
            last = Q.rear->data;
        }
    }
    printf("%d", sum);
}

20、二叉树–请设计一个算法,将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。

// 第二十题 使用递归实现对二叉树的中序遍历,在访问结点时,加上左右括号,根节点和叶子节点不加括号
void test20_1(BiTNode *p, int &deep){
    if(deep != 1 ) // 根节点不加括号
        if(p->lchild != NULL || p->rchild != NULL)  // 叶子结点不加括号
            printf("( ");
    if(p->lchild != NULL){
        deep++;
        test20_1(p->lchild, deep);
        deep--;
    }
    printf("%d ", p->data.value);
    if(p->rchild != NULL){
        deep++;
        test20_1(p->rchild, deep);
        deep--;
    }
    if(deep != 1)
        if(p->lchild != NULL || p->rchild != NULL)
            printf(") ");
}

void test20(){
    BiTree T;
    InitBiTree(T);
    buildTree(T);
    int deep = 1;
    test20_1(T, deep);
}

你可能感兴趣的:(数据结构,数据结构,链表,算法)