• 35.树与二叉树练习(1)(王道第5章综合练习)


    【所用的树,队列,栈的基本操作详见上一节代码】

    试题1(王道5.3.3节第3题):

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

    参考:34.二叉链树的C语言实现_北京地铁1号线的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_54708219/article/details/133581706

    试题2(王道5.3.3节第4题):

    给出二叉树自下而上,从右到左的层次遍历算法。

    这道题很显然就是层次遍历算法调个个,加个栈即可实现:

    1. //层次遍历(自下而上,从右到左)
    2. void LevelOrder2(BiTree T){
    3. Queue q;
    4. InitQueue(q);
    5. Sqstack S;
    6. InitStack(S);
    7. BiTree p = T;
    8. InsertQueue(q, p);
    9. while(!IsQueueEmpty(q)){
    10. p = DeleteQueue(q, p);
    11. InsertSqstack(S, p);
    12. if(p->lchild!=NULL)
    13. InsertQueue(q, p->lchild);
    14. if(p->rchild!=NULL)
    15. InsertQueue(q, p->rchild);
    16. }
    17. while(S.top != -1){
    18. p = DeleteSqstack(S, p);
    19. printf("%c", p->data);
    20. }
    21. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 二叉树的层次遍历序列是:ABCDE
    5. 二叉树的层次遍历(自下而上,自右向左)序列是:EDCBA

    试题3(王道5.3.3节第5题):

    设计非递归算法求二叉树的高度。

    此题采用层次遍历,附设一个指针a:

    1. //利用层次遍历实现非递归计算树的深度
    2. int LevelOrderDepth(BiTree T){
    3. int Depth = 0;
    4. if(!T)
    5. return Depth;
    6. else{
    7. Queue q;
    8. InitQueue(q);
    9. BiTree p = T;
    10. InsertQueue(q, p);
    11. Depth = 1;
    12. int a = q.rear; //指针a指向队尾,也就是这一层最后一个元素
    13. while(!IsQueueEmpty(q)){
    14. if(q.front==a){ //这个时候说明这一层出完了,此时rear就是下一行的末尾结点
    15. a = q.rear;
    16. Depth = Depth + 1;
    17. }
    18. p = DeleteQueue(q, p);
    19. if(p->lchild!=NULL)
    20. InsertQueue(q, p->lchild);
    21. if(p->rchild!=NULL)
    22. InsertQueue(q, p->rchild);
    23. }
    24. return Depth;
    25. }
    26. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 二叉树的深度是(非递归算法):3

    试题4(王道5.3.3节第6题):

    设一棵二叉树中各结点的值互不相同,其先序遍历序列和中序遍历序列分别存放在两个数组A和B中,试编写算法建立该二叉树的二叉链表。

    仍需要采用递归操作,这里要想办法根据前序序列找到根结点,然后在中序序列找根结点,从而确定哪些结点属于左子树,哪些结点属于右子树。

    1. //由先序序列和中序序列建立二叉树
    2. BiTree CreateBiTreeviaOrders(char a[],char b[],int x1,int y1,int x2,int y2){
    3. //x1,y1工作在前序序列中,x2,y2工作在中序序列中
    4. BiTree T;
    5. T = (BiTree)malloc(sizeof(BiTNode));
    6. T->data = a[x1]; //前序序列的第一个结点就是根结点
    7. int llen, rlen;
    8. for (int i = x2; i <= y2; i++){ //在中序序列找根结点
    9. if(b[i] == a[x1]){
    10. llen = i - x2; //左子树的序列长度(结点个数)
    11. rlen = y2 - i; //右子树的序列长度(结点个数)
    12. }
    13. }
    14. if (llen == 0)
    15. T->lchild = NULL;
    16. else
    17. T->lchild = CreateBiTreeviaOrders(a, b, x1 + 1, x1 + llen, x2, x2 + llen - 1);
    18. if (rlen == 0)
    19. T->rchild = NULL;
    20. else
    21. T->rchild = CreateBiTreeviaOrders(a, b, y1 - rlen + 1, y1, y2 - rlen + 1, y2);
    22. return T;
    23. }
    24. int main(){
    25. BiTree T;
    26. char a[6] = {'A', 'B', 'C', 'D', 'E', 'F'}; //先序序列
    27. char b[6] = {'C', 'B', 'A', 'E', 'D', 'F'}; //中序序列
    28. T = CreateBiTreeviaOrders(a, b, 0, 5, 0, 5); //初始必须是0和数组长度减一
    29. printf("该二叉树的后序遍历序列是:");
    30. PostOrderTraverse(T); //输出后序序列进行验证
    31. return 0;
    32. }

    这里以王道5.3.3节单选15题进行验证,输出结果就是A选项。

    该二叉树的后序遍历序列是:CBEFDA

    试题5(王道5.3.3节第7题):

    二叉树按二叉链表存储,写一个判别给定二叉树是否是完全二叉树的算法。

    此题的思路是借助层次遍历和队列,当队列中输出空结点的时候,如果此时队列还有非空结点说明不是完全二叉树。注意这里输入和验证的都是扩展二叉树,所以去掉了层次遍历中的结点非空判断。

    1. //判断是否是完全二叉树
    2. bool IfCompleteTree(BiTree T){
    3. Queue q;
    4. InitQueue(q);
    5. BiTree p = T;
    6. if(!p)
    7. return true;
    8. InsertQueue(q, p);
    9. while(!IsQueueEmpty(q)){
    10. p = DeleteQueue(q, p);
    11. if(p!=NULL){
    12. InsertQueue(q, p->lchild);
    13. InsertQueue(q, p->rchild);
    14. }
    15. else{
    16. int a = q.front;
    17. while(a!=q.rear){
    18. if(q.data[a]!=NULL)
    19. return false;
    20. a = (a + 1) % MAXSIZE;
    21. }
    22. return true;
    23. }
    24. }
    25. }
    26. int main(){
    27. BiTree T;
    28. printf("输入二叉树的前序序列,#代表空子树:\n");
    29. CreateBiTree(T);
    30. printf("二叉树创建成功!\n");
    31. printf("该二叉树是否是完全二叉树?%d", IfCompleteTree(T));
    32. return 0;
    33. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 该二叉树是否是完全二叉树?1
    5. 输入二叉树的前序序列,#代表空子树:
    6. AB##CD##E##
    7. 二叉树创建成功!
    8. 该二叉树是否是完全二叉树?0

    试题6(王道5.3.3节第8题):

    设二叉树采用二叉链表存储结构,设计算法计算给定二叉树的双分支结点个数。

    递归算法:

    1. //判断是否是完全二叉树
    2. int TwobranchNodes(BiTree T){
    3. if(T==NULL)
    4. return 0;
    5. else if(T->lchild!=NULL&&T->rchild!=NULL)
    6. return TwobranchNodes(T->lchild) + TwobranchNodes(T->rchild) + 1;
    7. else
    8. return TwobranchNodes(T->lchild) + TwobranchNodes(T->rchild);
    9. }

    非递归算法(层次遍历逐个结点检查):

    1. //判断是否是完全二叉树
    2. int TwobranchNodes(BiTree T){
    3. int a = 0;
    4. Queue q;
    5. InitQueue(q);
    6. BiTree p = T;
    7. InsertQueue(q, p);
    8. while(!IsQueueEmpty(q)){
    9. p = DeleteQueue(q, p);
    10. if(p->lchild!=NULL && p->rchild!=NULL)
    11. a = a + 1;
    12. if(p->lchild!=NULL)
    13. InsertQueue(q, p->lchild);
    14. if(p->rchild!=NULL)
    15. InsertQueue(q, p->rchild);
    16. }
    17. return a;
    18. }

    试题7(王道5.3.3节第9题):

    设树B是一棵采用链式结构存储的二叉树,编写一个把树B中所有结点的左右子树进行交换的函数。

    此题同题6一样也可以用递归或层次遍历的方法,这里给出非递归的方法:

    1. //把二叉树的左右子树交换
    2. int ChangeTwobranch(BiTree &T){
    3. Queue q;
    4. InitQueue(q);
    5. BiTree p = T;
    6. BiTree r;
    7. InsertQueue(q, p);
    8. while(!IsQueueEmpty(q)){
    9. p = DeleteQueue(q, p);
    10. if(p->lchild!=NULL || p->rchild!=NULL){
    11. r = p->lchild;
    12. p->lchild = p->rchild;
    13. p->rchild = r;
    14. }
    15. if (p->lchild != NULL)
    16. InsertQueue(q, p->lchild);
    17. if(p->rchild!=NULL)
    18. InsertQueue(q, p->rchild);
    19. }
    20. return 0;
    21. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 该二叉树的层次遍历序列是:ACBED

    试题8(王道5.3.3节第10题):

    假设二叉树采用二叉链存储结构存储,设计算法求先序遍历序列中第k个结点的值。

    这里使用一个计数器即可:

    1. //输出前序遍历的第x个元素
    2. void PreOrderx(BiTree T,int x){
    3. int a = 0;
    4. BiTree p = T; //p是遍历指针
    5. Sqstack S;
    6. InitStack(S);
    7. while(p != NULL|| !IsStackEmpty(S)){
    8. if(p){
    9. a = a + 1;
    10. if(a == x){
    11. printf("第%d个元素是:%c", x, p->data);
    12. break;
    13. }
    14. InsertSqstack(S, p);
    15. p = p->lchild;
    16. }
    17. else{
    18. p = DeleteSqstack(S, p);
    19. p = p->rchild;
    20. }
    21. }
    22. }

    当然也可以采用递归,注意这里的计数器必须写在全局变量里,否则每次调用递归都会从零开始:

    1. //输出前序遍历的第x个元素
    2. int a = 0; //计数器
    3. void PreOrderx(BiTree T,int x){
    4. if (T!=NULL){
    5. a = a + 1;
    6. if(a==x)
    7. printf("前序遍历序列的第%d个元素是:%c", x, T->data);
    8. PreOrderx(T->lchild,x);
    9. PreOrderx(T->rchild,x);
    10. }
    11. }
    12. int main(){
    13. BiTree T;
    14. printf("输入二叉树的前序序列,#代表空子树:\n");
    15. CreateBiTree(T);
    16. printf("二叉树创建成功!\n");
    17. PreOrderx(T, 3);
    18. return 0;
    19. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 前序遍历序列的第3个元素是:D

    试题9(王道5.3.3节第11题):

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

    仍然是与层次遍历结合:

    1. //这个函数用来删除树T
    2. void Free(BiTree &T){
    3. if(T!=NULL){
    4. Free(T->lchild);
    5. Free(T->rchild);
    6. free(T);
    7. }
    8. }
    9. //对值为x的结点,删除以它为根的子树
    10. void Freex(BiTree &T,char x){
    11. Queue q;
    12. InitQueue(q);
    13. BiTree p = T;
    14. InsertQueue(q, p);
    15. while(!IsQueueEmpty(q)){
    16. p = DeleteQueue(q, p);
    17. if(p->data == x){
    18. Free(p->lchild); //这样写保留了当前结点
    19. Free(p->rchild);
    20. p->lchild = NULL;
    21. p->rchild = NULL;
    22. }
    23. if(p->lchild!=NULL)
    24. InsertQueue(q, p->lchild);
    25. if(p->rchild!=NULL)
    26. InsertQueue(q, p->rchild);
    27. }
    28. }
    29. int main(){
    30. BiTree T;
    31. printf("输入二叉树的前序序列,#代表空子树:\n");
    32. CreateBiTree(T);
    33. printf("二叉树创建成功!\n");
    34. printf("当前二叉树的层次遍历序列是:");
    35. LevelOrder(T);
    36. printf("\n");
    37. Freex(T, 'B'); //删除以B为根结点的树
    38. printf("当前二叉树的层次遍历序列是:");
    39. LevelOrder(T);
    40. printf("\n");
    41. return 0;
    42. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 当前二叉树的层次遍历序列是:ABCDE
    5. 当前二叉树的层次遍历序列是:ABC
    6. 输入二叉树的前序序列,#代表空子树:
    7. ABD###CE##F##
    8. 二叉树创建成功!
    9. 当前二叉树的层次遍历序列是:ABCDEF
    10. 当前二叉树的层次遍历序列是:ABCEF

    试题10(王道数据结构5.3.3节第12题):

    编写算法打印值为x的结点的所有祖先,假设值为x的结点不多于一个。

    此题的算法十分典型:它用的是非递归后序遍历算法,这种算法需要借助栈来实现,当访问到值为x的结点的时候,栈中所有元素就是该结点的祖先,依次打印输出即可。有关非递归后序遍历算法的代码在上一节。

    1. //寻找给定结点的所有祖先结点,采用后续遍历的非递归算法
    2. void FindParents(BiTree T,char x){
    3. Sqstack S;
    4. InitStack(S);
    5. BiTree p = T;
    6. BiTree r = NULL; //r用来记录访问结点的前一个结点
    7. while(p||!IsStackEmpty(S)){
    8. if(p){
    9. InsertSqstack(S, p);
    10. p = p->lchild;
    11. }
    12. else{
    13. p = S.data[S.top]; //读栈顶元素(但不出栈)
    14. if(p->rchild&&p->rchild!=r){
    15. p = p->rchild;
    16. }
    17. else{
    18. p = DeleteSqstack(S, p);
    19. if(p->data == x){
    20. printf("%c", p->data);
    21. break;
    22. }
    23. r = p;
    24. p = NULL;
    25. }
    26. }
    27. }
    28. while(!IsStackEmpty(S)){ //这个时候栈里的元素全部是结点的祖先
    29. p = DeleteSqstack(S, p);
    30. printf("%c", p->data);
    31. }
    32. }
    33. int main(){
    34. BiTree T;
    35. printf("输入二叉树的前序序列,#代表空子树:\n");
    36. CreateBiTree(T);
    37. printf("二叉树创建成功!\n");
    38. printf("当前二叉树的层次遍历序列是:");
    39. LevelOrder(T);
    40. printf("\n");
    41. printf("当前二叉树中结点E的祖先结点是:");
    42. FindParents(T, 'E');
    43. return 0;
    44. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD##E##C##
    3. 二叉树创建成功!
    4. 当前二叉树的层次遍历序列是:ABCDE
    5. 当前二叉树中结点E的祖先结点是:EBA

    试题11(王道数据结构5.3.3节第13题):

    给出二叉链树中两个结点的指针p和q,试编写算法求解p和q的公共祖先结点r。

    此题和上一题很像,分别求出p和q的祖先然后比较即可。

    1. //寻找给定结点的所有祖先结点,采用后续遍历的非递归算法,和上一题不同的是,本题以栈的形式返回
    2. Sqstack FindParents(BiTree T,char x){
    3. Sqstack S;
    4. InitStack(S);
    5. BiTree p = T;
    6. BiTree r = NULL; //r用来记录访问结点的前一个结点
    7. while(p||!IsStackEmpty(S)){
    8. if(p){
    9. InsertSqstack(S, p);
    10. p = p->lchild;
    11. }
    12. else{
    13. p = S.data[S.top]; //读栈顶元素(但不出栈)
    14. if(p->rchild&&p->rchild!=r){
    15. p = p->rchild;
    16. }
    17. else{
    18. p = S.data[S.top];
    19. if(p->data == x){
    20. break;
    21. }
    22. p = DeleteSqstack(S, p);
    23. r = p;
    24. p = NULL;
    25. }
    26. }
    27. }
    28. return S;
    29. }
    30. //有了两个栈我们就可以遍历然后找到祖先结点了
    31. //注意这里最差也能返回整棵二叉树的根结点,或者返回其中一个结点时,代表一个结点就是另一个结点的祖先
    32. BiTree FindSameParents(BiTree T,char a,char b){
    33. Sqstack S1 = FindParents(T, a);
    34. Sqstack S2 = FindParents(T, b);
    35. int same = -1; //same用来遍历两个栈,并指向最后一个相同的祖先结点
    36. while(S1.data[same+1] == S2.data[same+1]){
    37. same = same + 1;
    38. }
    39. printf("%c", S1.data[same]->data);
    40. return S1.data[same];
    41. }
    42. int main(){
    43. BiTree T;
    44. printf("输入二叉树的前序序列,#代表空子树:\n");
    45. CreateBiTree(T);
    46. printf("二叉树创建成功!\n");
    47. printf("当前二叉树的层次遍历序列是:");
    48. LevelOrder(T);
    49. printf("\n");
    50. printf("当前二叉树中结点E,F的祖先结点是:");
    51. FindSameParents(T, 'E', 'F'); //求E,F的祖先结点
    52. return 0;
    53. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABD###CE##F##
    3. 二叉树创建成功!
    4. 当前二叉树的层次遍历序列是:ABCDEF
    5. 当前二叉树中结点E,F的祖先结点是:C

    试题12(王道数据结构5.3.3节第14题):

    假设二叉树采用二叉链表存储,设计一个算法求非空二叉树的宽度(也就是结点数最多的那一层的结点个数)。

    此题仍然可以利用层次遍历把每层的结点数输出,存在一个数组里面然后找出最大值:

    1. //求非空二叉树的宽度,借助层次遍历把每层的结点数都求出来
    2. int LevelOrderWidth(BiTree T){
    3. Queue q;
    4. InitQueue(q);
    5. BiTree p = T;
    6. InsertQueue(q, p);
    7. int a = q.front; //a指针指向这一层的第一个结点
    8. int b = q.rear; //b指针指向这一层的第一个结点
    9. int num = 1; //输出这一层的结点个数
    10. int numarray[10]; //把各层的结点个数存在一个数组里
    11. int depth = 0; //深度,实际深度是depth+1,因为numarray数组下标从0开始
    12. numarray[depth] = num;
    13. while(!IsQueueEmpty(q)){
    14. if(q.front == b){ //说明这一层出完了
    15. a = q.front; //a指向下一层第一个结点
    16. b = q.rear; //b指向下一层最后一个结点
    17. num = (b - a > 0) ? (b - a) : (b - a + MAXSIZE); //循环队列三目运算符判断
    18. depth = depth + 1;
    19. numarray[depth] = num;
    20. }
    21. p = DeleteQueue(q, p);
    22. if(p->lchild!=NULL)
    23. InsertQueue(q, p->lchild);
    24. if(p->rchild!=NULL)
    25. InsertQueue(q, p->rchild);
    26. }
    27. //到此numarray存储了每层的结点数,接下来找其中的最大值输出
    28. num = 1;
    29. for (int i = 0; i <= depth;i++){
    30. printf("%d", numarray[i]);
    31. if(numarray[i] > num)
    32. num = numarray[i];
    33. }
    34. return num;
    35. }
    36. int main(){
    37. BiTree T;
    38. printf("输入二叉树的前序序列,#代表空子树:\n");
    39. CreateBiTree(T);
    40. printf("二叉树创建成功!\n");
    41. printf("当前二叉树的层次遍历序列是:");
    42. LevelOrder(T);
    43. printf("\n");
    44. printf("各层的结点数是:");
    45. printf("二叉树的宽度是:%d",LevelOrderWidth(T));
    46. return 0;
    47. }

    输出:

    1. 输入二叉树的前序序列,#代表空子树:
    2. ABDF##G##E##CH###
    3. 二叉树创建成功!
    4. 当前二叉树的层次遍历序列是:ABCDEHFG
    5. 各层的结点数是:1232二叉树的宽度是:3

    试题13(王道数据结构5.3.3节第15题):

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

    此题和前面的试题4也就是根据前序序列和中序序列建立二叉树的算法高度相似,也是要用递归解决,甚至此题比试题4简单,因为满二叉树左右子树的长度必然一样。

    1. //根据满二叉树的前序序列求后序序列
    2. void PreToPost(ElemType pre[],int l1,int h1,ElemType post[],int l2,int h2){
    3. //l1和h1工作在pre前序序列,l2和h2工作在post后序序列
    4. if(h1>=l1){
    5. post[h2] = pre[l1];
    6. int length = (h1 - l1) / 2;
    7. PreToPost(pre, l1 + 1, l1 + length, post, l2, l2 + length - 1);
    8. PreToPost(pre, l1 + length + 1, h1, post, l2 + length, h2 - 1);
    9. }
    10. }
    11. int main(){
    12. ElemType pre[7] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    13. ElemType post[7];
    14. PreToPost(pre, 0, 6, post, 0, 6);
    15. printf("后序序列是:");
    16. for (int i = 0; i <= 6; i++){
    17. printf("%c", post[i]);
    18. }
    19. return 0;
    20. }

    输出:

    后序序列是:CDBFGEA
  • 相关阅读:
    结合protobuf和socket实现多进程通讯
    Qwt开发笔记(一):Qwt简介、下载以及基础demo工程模板
    py代码-python异步请求
    数据结构 -栈和队列
    Windows Defender防火墙配置错误与GPO:梳理关键点
    数学建模学习(89):交叉熵优化算法(CEM)对多元函数寻优
    Android Studio :can not resolve symbol ‘List‘
    [附源码]计算机毕业设计springboot春晓学堂管理系统
    查杀Linux服务器病毒进程并对Linux中的文件描述符FD进行简单探索
    Android 100元平板也能吃鸡玩王者!小米平板刷机神盾dot1.2保姆级教程。
  • 原文地址:https://blog.csdn.net/qq_54708219/article/details/133654147