2024王道数据结构大题p143-p144(二)

文章介绍了多种二叉树相关算法,包括寻找值为x的结点的所有祖先结点、找到两个结点的最近公共祖先、非递归后序遍历、求二叉树宽度、满二叉树的先序序列转后序序列、构建叶结点链表以及判断两棵二叉树是否相似等。这些算法通过不同的数据结构和遍历策略解决二叉树问题。
摘要由CSDN通过智能技术生成

二叉树

12.在二叉树中查找值为x的结点,试编写算法打印值为x的结点的所有祖先结点,假设值为x的结点不多于一个。

  • 算法的基本设计思想
    采用非递归的后序遍历,利用辅助栈,当弹出栈顶元素判断值是否为x,然后此时栈中元素即为x结点的祖先结点
  • 算法实现
// 在二叉树中查找值为x的结点,试编写算法打印值为x的结点的所有祖先结点
#include <iostream>
using namespace std;
#define Max 10
typedef struct treenode
{
    char data;
    treenode *lchild, *rchild;
} treenode, *tree;
void buildtree(tree &t)
{
    char ch;
    ch = getchar();
    if (ch == '#')
        t = NULL;
    else
    {
        t = (tree)malloc(sizeof(treenode));
        t->data = ch;
        t->rchild = NULL;
        t->lchild = NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
typedef struct stack
{
    treenode *t;
    int tag;//tag=0表示左孩子被访问,tag=1表示右孩子被访问
} stack;

void rexfather(tree &t, char x)
{
   if(t==NULL) cout<<"未找到"<<endl;
    // 初始化栈
    stack s[100];
    int top=0;
    while (t!=NULL||top>0)//树空栈空就退出
    {
            while(t!=NULL&&t->data!=x) //沿着左分支向下压入栈中
            {
                s[++top].t=t;
                s[top].tag =0;  
                t=t->lchild; 
            }
            if(t!=NULL&&t->data==x)//找到x
            {
                cout<<"所查结点的所有祖先结点的值为:"<<endl;
                for(int i=1;i<=top;i++)//栈中元素都是祖先结点,
                {
                    printf("%c",s[i].t->data);
                }
                break;
            }
            while(top!=0&&s[top].tag==1)//栈顶元素的左右孩子都被访问了退栈
            top--;
            if(top!=0)
            {
                s[top].tag=1;
                t=s[top].t->rchild;
            }
    }   
    
}
int main()
{
    tree t;
    buildtree(t);
    rexfather(t,'G');

    return 0;
}//ABD##E##CF##G##
  • 小结

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

  • 算法的基本设计思想
    用两个栈,假设p在q左边先把p的祖先压栈s1,然后复杂这个栈到s2,然后找到p,将s1与s2匹配第一个相同的就是刚刚祖先r。

  • 算法实现

// 设一颗二叉树的结点结构为(LLINK,INFO,RLINK),ROOT为指向该二叉树根结点的指针,p和q分别为指向该二叉树中任意两个结点的
// 指针,试编写算法ANCESTOR(ROOT,p,q,r)找到p和q的最近的公共祖先结点r.
#include <iostream>
using namespace std;
typedef struct treenode
{
    char data;
    treenode *lchild, *rchild;
} treenode, *tree;
void buildtree(tree &t)
{
    char ch;
    ch = getchar();
    if (ch == '#')
        t = NULL;
    else
    {
        t = (tree)malloc(sizeof(treenode));
        t->data = ch;
        t->lchild = NULL;
        t->rchild = NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
typedef struct stack
{
    treenode *t;
    int tag; // tag=0 左孩子被访问过,tag=1右孩子被访问过
} stack;
tree Ancestor(tree &t, treenode *p, treenode *q)
{
    stack s1[10], s2[10];
    int top1 = 0, top2 ;
    while (t != NULL || top1 > 0) // 树和栈都空跳出
    {
        while (t != NULL) // 沿着左分支压入栈中
        {
            s1[++top1].t = t;
            s1[top1].tag = 0;
            t = t->lchild;
        }
        while (top1 != 0 && s1[top1].tag == 1) // 假定p是在q的左测
        {
            if (s1[top1].t == p) // 找到p后,栈中元素都是p 的祖先
            {
                for (int i = 1; i <= top1; i++)
                    s2[i] = s1[i]; // 将栈s1复制到栈s2
                    top2=top1;
            }
            if (s1[top1].t == q) // 找到q结点
            {
                for (int i = top1; i > 0; i--) // 将栈中元素的结点到s2中去匹配
                    for (int j = top2; j > 0; j--)
                        if (s2[j].t == s1[i].t)
                            return s1[i].t; //找到了
            }
            top1--;    // 退栈
        }              // while
        if (top1 != 0)  //压右边
        {
            s1[top1].tag = 1;
            t = s1[top1].t->rchild; // 工作指针t指向栈顶的右孩子     沿右分支向下遍历
        }
    }            // while
    return NULL; // 前面都没返回那就是没找到,p和q无公共结点;
}

int main()
{
    tree t;
    buildtree(t);
    treenode *p = t->lchild->lchild, *q = t->lchild->rchild;
    cout << Ancestor(t, p, q)->data << endl;
    return 0;
}
// ABD##E##CF##G##
  • 小结
    用了tag位,tag=0 左孩子被访问过,tag=1右孩子被访问过;
    和双栈。

14.编写后序遍历二叉树的非递归算法

  • 算法的基本设计思想

  • 算法实现


  • 小结## 1.编写后序遍历二叉树的非递归算法

15. 假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度(即具有结点数最多的那一层的结点个数)

  • 算法的基本设计思想
    求宽度,最多那一层我们想到用层次遍历,求出每一层的宽度,然后比较大小;
  • 算法实现
// 假设二叉树采用二叉链表存储结构,设计一个算法,求非空二叉树b的宽度(即具有结点数最多的那一层的结点个数)
// 算法思路,求宽度,最多那一层我们想到用层次遍历,求出每一层的宽度,然后比较大小;
#include <iostream>
using namespace std;
#define Max 20
typedef struct treenode
{
    char data;
    treenode *rchild, *lchild;
} treenode, *tree;
void buildtree(tree &t)
{
    char ch;
    ch = getchar();
    if (ch == '#')
        t = NULL;
    else
    {
        t = (tree)malloc(sizeof(treenode));
        t->data = ch;
        t->rchild = NULL;
        t->lchild = NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
// 队列结构
typedef struct queue
{
    treenode *data[Max];
    int level[Max]; // data中下标结点的层次
    int f, r;
} queue;
int Btwidth(tree &t)
{
    queue q;           // 队列
    tree p;            // 保存出队的结点指向
    int k;             // 保存出队的结构层次
    q.f = q.r = -1;    // 头尾指针的初始化
    q.data[++q.r] = t; // 根节点入队
    q.level[q.r] = 1;  // 刚入队的根结点的层次记为1;
    while (q.f < q.r)  // 层次遍历的循环条件,尾指针大于头指针
    {
        // 头指针出队;
        p = q.data[++q.f]; // p指针保存刚刚出队的结点
        k = q.level[q.f];  // 保存出队结点的层次;
        if (p->lchild)     // 有左孩子左孩子入队
        {
            q.data[++q.r] = p->lchild; // 左孩子入队
            q.level[q.r] = k + 1;      // 进入队列的结点的层次为出队的结点的层次+1;
        }
        if (p->rchild)
        {
            q.data[++q.r] = p->rchild;
            q.level[q.r] = k + 1;
        }
    }
    // 遍历的时候最大值更新,遍历下标,计算每层的个数
    int maxx = 0, i = 0, n;
    k = 1;
    while (i <= q.r) // 遍历的下标小于队尾部
    {
        n = 0; // 一开始每层默认0个
        while (i <= q.r && q.level[i] == k)
        {
            n++;
            i++;
        }
        k = q.level[i];
        if (n > maxx)
            maxx = n;
    }
    return maxx;
}
int main()
{
    tree t;
    buildtree(t);
    cout << Btwidth(t) << endl;
    return 0;
}
// ABD##E##CF##G##
  • 小结## 1.编写后序遍历二叉树的非递归算法

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

  • 算法的基本设计思想
    用递归,pre的第一个结点就是post的最后一个结点,然后每次去找到half=将剩下的结点划分为两部分去递归
  • 算法实现
// 设有一颗满二叉树(所有结点均不同),已知其先序序列为pre,设计一个算法求出其后序序列post。
#include <iostream>
using namespace std;
void prepost(char pre[], int l1, int r1, char post[], int l2, int r2) // 数组就是给地址
{
    int half;
    if (l1 <= r1)//左边大于右边就退出递归, 递归条件是左边小于等于右边才能去左右切割递归;
    {
        post[r2] = pre[l1];
        half = (r1 - l1) / 2;
        prepost(pre, l1 + 1, l1 + half, post, l2, l2 + half - 1);
        prepost(pre, l1 + half + 1, r1, post, l2 + half, r2 - 1);
    }
}
int main()
{
    char pre[8] = "ABDECFG", post[8];
    prepost(pre, 0, 6, post, 0, 6);
    for (int i = 0; i < 7; i++)
    {
        cout << post[i] << " ";
    }
    return 0;
}
  • 小结
    做这种题目先想到递归,然后大树化小数找中间的分界点,和结点之间的逻辑关系
  • 16.设计一个算法将二叉树的叶结点按从左到右的顺序连城一个单链表,表头指针为head,二叉树 按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表

  • 算法的基本设计思想
    用中序递归遍历 (左根右)
  • 算法实现
//设计一个算法将二叉树的叶结点按从左到右的顺序连城一个单链表,表头指针为head,
//二叉树 按二叉链表方式存储,链接时用叶结点的右指针域来存放单链表
//由题目很容易想到用中序递归遍历。
#include <iostream>
using namespace std;
typedef struct treenode {
    char data;
    treenode *lchild,*rchild;

}treenode,*tree;
void buildtree(tree &t)
{
    char ch;
    ch=getchar();
    if(ch=='#') t=NULL;
    else{
        t=(tree)malloc(sizeof(treenode ));
        t->data=ch;
        t->lchild=NULL;
        t->rchild=NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
void disp(tree &t)
{
    if(t)
    {
        cout<<t->data<<" ";
        disp(t->lchild);
        disp(t->rchild);
    }
}
tree head=(tree)malloc(sizeof(treenode)),pre=NULL;//初始化一个head结点,和一个前驱结点用来保存下一个结点的前驱结点初始为NULL;

tree Inorder(tree &t)//最后返回后head
{   
    if(t)//递归条件
    {
            Inorder(t->lchild);//找最左下的结点
            if(t->rchild==NULL&&t->rchild==NULL)//判断这个最左下的结点是不是叶子结点
            {
                //判断这个叶子结点是不是第一个叶子结点,前驱是NULL就是第一个
                if(pre==NULL)
                    {
                        head=t;
                        pre=t;
                    }
                else
                    {
                        pre->rchild=t;
                        pre=t;
                    }

            }
            Inorder(t->rchild);//再去右子树找
            t->rchild=NULL;  //注意这里需要将最后一个结点的右孩子指向空
    }
    return head;
}
int main(){
    tree t;
    buildtree(t);
    disp(t);
    Inorder(t);
     while(head)
   {
       cout<<head->data<<" ";
       head=head->rchild;
   }
    return 0;
}//ABD##E##CF##G##
  • 小结
    注意最后结点需要指向空
  • 17.设计判断两颗二叉树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;或者T1的左子树和T2的右子树是相似的,且T1的右子树和T2的右子树是相似的。

  • 算法的基本设计思想
    相似: 空和空 || 非空和非空
    不相似 : 空 和非空
    递归判断左边和右边,用int返回类型的递归函数
  • 算法实现
/* 设计判断两颗二叉树是否相似的算法。所谓二叉树T1和T2相似,指的是T1和T2都是空的二叉树或都只有一个根节点;
或者T1的左子树和T2的右子树是相似的,且T1的右子树和T2的右子树是相似的。 */
#include <iostream>
using namespace std;
typedef struct treenode
{
    char data;
    treenode *rchild, *lchild;
} treenode, *tree;

void buildtree(tree &t)
{
    char ch;
   cin>>ch;
    if (ch == '#')
        t = NULL;
    else
    {
        t = (tree)malloc(sizeof(treenode));
        t->data = ch;
        t->rchild = NULL;
        t->lchild = NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
int similar(tree &t1, tree &t2)
{
    int left,right;
    if (t1 == NULL && t2 == NULL)
        return 1;
    else if (t1==NULL||t2==NULL)
        return 0;
    else
    {
         left = similar(t1->lchild, t2->lchild);
         right = similar(t1->rchild, t2->rchild);
    }
    return (left && right);
}
int main()
{
    tree t1, t2;
    cout << "创建第一个二叉树:" << endl;
    buildtree(t1);
    cout << "创建第二个二叉树:" << endl;
    buildtree(t2);
    if (similar(t1, t2))
        cout << "相似" << endl;
    else
        cout << "不相似" << endl;
    return 0;
}
  1. 小结
    用left 记录左边 right记录右边
  2. 18.写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法

  3. 算法的基本设计思想
    先对二叉树中序线索化
    左根右,左右根,

后序的前驱:q

1)p有右孩子,q->p
2)else	if	p有左孩子 ,	q->p
3)else	if	p的左孩子是空	q = NULL
4)else	p的最上祖先的左孩子存在 q指向它  没有就是NULL
  1. 算法实现
/* 写出在中序线索二叉树里查找指定结点在后序的前驱结点的算法 */
#include <iostream>
using namespace std;
typedef struct treenode
{
    char data;
    treenode *rchild, *lchild;
    int ltag, rtag;
} treenode, *tree;
void buildtree(tree &t)
{
    char ch;
    cin >> ch;
    if (ch == '#')
        t = NULL;
    else
    {
        t = (tree)malloc(sizeof(treenode));
        t->data = ch;
        t->ltag = 0;
        t->rtag = 0;
        t->lchild = NULL;
        t->rchild = NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
void zx(tree &t, treenode *&pre)
{
    if (t)
    {
        zx(t->lchild, pre);
        if (t->lchild == NULL)
        {
            t->lchild = pre;
            t->ltag = 1;
        }

        if (pre != NULL && pre->rchild == NULL)
        {
            pre->rchild = t;
            pre->rtag = 1;
        }
        pre = t;
        zx(t->rchild, pre);
    }
}
tree fpostpre(tree &t, treenode *p)
{
    treenode *q;
    if (p->rtag == 0)
        q = p->rchild; // 有右孩子
    else if (p->ltag == 0)
        q = p->lchild; // 有左孩子
    else if (p->lchild == NULL)
        q = NULL; // 中序第一个结点
    else
    {

        while (p->ltag == 1 && p->lchild != NULL)
            p = p->lchild; // 沿着线索找到最上祖先结点
        if (p->lchild)
            q = p->lchild; // 如果这个祖先结点有左孩子,q=它
        else
            q = NULL;
    }

    return q;
}
int main()
{
    tree t;
    buildtree(t);
    treenode *pre = NULL;
    zx(t, pre);
    cout << fpostpre(t, t->rchild)->data << endl;
    return 0;
} // ABD##E##CF##G##
  • 小结
    分析好后序的前驱结点的存在可能
  • 19.《2014统考真题》二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和,给定一颗二叉树T,采用二叉链表存储,结点结构为 left|weight|right 其中叶结点的weight域保存该结点的非负权指,

设root为指向T的根节点的指针,请设计求T的WPL算法

  • 算法的基本设计思想
    递归
  • 算法实现
/* 《2014统考真题》二叉树的带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和,
给定一颗二叉树T,采用二叉链表存储,结点结构为 left|weight|right 其中叶结点的weight域保存该结点的非负权指,
设root为指向T的根节点的指针,请设计求T的WPL算法*/
#include <iostream>
using namespace std;
typedef struct treenode {
    char data;
    treenode *rchild,*lchild;
}treenode ,*tree;
void buildtree(tree &t)
{
    char ch;
    cin >>ch;
    if(ch=='#') t=NULL;
    else{
        t=(tree)malloc(sizeof(treenode ));
        t->data=ch;
        t->lchild=NULL;
        t->rchild=NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
int wpl(tree &t,int deep )
{
    static int ans=0;
    if(t->lchild==NULL&&t->rchild==NULL)//是叶子结点
    {
     return    ans+=(deep*((t->data)-'0'));
    }
    if(t->lchild)
        wpl(t->lchild,deep+1);
    if(t->rchild)
        wpl(t->rchild,deep+1);
    return ans;
}   
int main(){
    tree t;
    buildtree(t);
    cout<<wpl(t,0)<<endl;
    return 0;
}
/*

             1                            
          2    3                    
        4  5  6  7                   
124##5##36##7##     
ans=(4+5+6+7)*2=44

*/
  • 小结
    ans+=(deep*((t->data)-‘0’)) 字符减去’0’就是数字
  • 20.《2017统考真题》请设计一个算法,将给定的表达树(二叉树)转换为等价的中缀表达式

(通过括号反映操作符的计算次序)并输出

  • 算法的基本设计思想
    从树的根节点左孩子用括号括起来,右孩子用括号括起来,叶子结点不用括,中序递归
  • 算法实现
/* 《2017统考真题》请设计一个算法,将给定的表达树(二叉树)转换为等价的中缀表达式
(通过括号反映操作符的计算次序)并输出 */
#include <iostream>
using namespace std;
typedef struct treenode
{
    char data;
    treenode *rchild, *lchild;

} treenode, *tree;
void buildtree(tree &t)
{
    char ch;
    cin >> ch;
    if (ch == '#')
        t = NULL;
    else
    {
        t = (tree)malloc(sizeof(treenode));
        t->data = ch;
        t->lchild = NULL;
        t->rchild = NULL;
        buildtree(t->lchild);
        buildtree(t->rchild);
    }
}
void toexp(tree &t, int deep)
{
    if (t == NULL)
        return;                                      // 树空直接访回
    else if (t->lchild == NULL && t->rchild == NULL) // 叶子结点输出data
    {
        cout << t->data;
    }
    else // 有左右孩子进行递归
    {
        if (deep > 1)
            cout << '(';
        if (t->lchild)//左
            toexp(t->lchild, deep + 1);
        cout << t->data;//print计算符
        if (t->rchild)//右
            toexp(t->rchild, deep + 1);
        if (deep > 1)
            cout << ')';
    }
}
int main()
{
    tree t;
    buildtree(t);
    toexp(t, 1);
    return 0;
}

/*

        *
    +       *
  a   b   c  -
  *+a##b##*c##-#d##
  • 小结
    递归
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿酱x

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

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

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

打赏作者

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

抵扣说明:

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

余额充值