• 二叉树的最大宽度


    题目:

    给定一个二叉树,计算二叉树的最大宽度(二叉树的最大宽度是指二叉树所有层次中结点个数的最大值)。

    分析:想到求一个二叉树的最大宽度,不难想到层序遍历,即采用入队和出队的方式来寻找二叉树的最大宽度,ok,话不多说,直接开整~


    • 首先定义一个树的结构体

    1. //树的结构体
    2. typedef struct BinNode{
    3. char data;
    4. struct BinNode *lchild,*rchild;
    5. }BinNode,*BinTree;
    6. typedef BinTree ElemType;//把队列的元素定义为树

    lchild和rchild分别代表树的左孩子和右孩子,BinTree为结构体指针;typedef BinTree ElemType;主要是我们需要把树的结点放到队列里面,,所以把队列的元素定义为树~ 

    • 利用先序遍历来创建一个二叉树 

    1. void CreateBinTree(BinTree *T)
    2. {
    3. char ch;
    4. if((ch=getchar())=='#') *T=NULL;
    5. else
    6. {
    7. (*T)=(BinTree)malloc(sizeof(BinNode));
    8. (*T)->data=ch;
    9. CreateBinTree((BinTree *)&((*T)->lchild));//强制类型转换 (BinTree *)
    10. CreateBinTree((BinTree *)&((*T)->rchild));
    11. }
    12. printf("创建成功!\n");
    13. }

     由于T本身就是指针类型,再传入一个指针即(**T),所以对T进行解引用操作;递归调用函数时(BinTree *)表示强制类型转换。用'#'表示空

     此时树的操作我们已经差不多搞完了,现在要考虑一下什么是树的层次遍历。

    由于层次遍历我们需要用到队列,所以我们先写出关于队列的一系列操作~

    • 队列的操作

    1. //队
    2. typedef struct Queue{
    3. ElemType data[MAXSIZE];
    4. int front,rear;
    5. }Queue,*PtrQueue;
    6. //初始化队列
    7. void InitQueue(PtrQueue Q)
    8. {
    9. Q->front=Q->rear=0;
    10. memset(Q->data,0,sizeof(ElemType)*MAXSIZE);
    11. }
    12. //判满
    13. int IsFull(PtrQueue Q)
    14. {
    15. if ((Q->rear+1) % MAXSIZE == Q->front) return 1;
    16. return 0;
    17. }
    18. //判空
    19. int IsEmpty(PtrQueue Q)
    20. {
    21. if (Q->rear == Q->front) return 1;
    22. return 0;
    23. }
    24. //入队
    25. void EnQueue(PtrQueue Q,ElemType *e)
    26. {
    27. if(!Q || !e) return;//队为空或者树结点为空
    28. if(!IsFull(Q))
    29. {
    30. memcpy(&Q->data[Q->rear],e,sizeof(ElemType));
    31. Q->rear=(Q->rear+1)%MAXSIZE;
    32. }
    33. }
    34. //出队
    35. void DeQueue(PtrQueue Q,ElemType *e)
    36. {
    37. if(!Q) return;
    38. if(!IsEmpty(Q))
    39. {
    40. memcpy(e,&Q->data[Q->front],sizeof(ElemType));
    41. Q->front=(Q->front+1)%MAXSIZE;
    42. }
    43. }

    队列的顺序存储就不过多介绍了,这里简单介绍一下memcpy函数和memset函数,两个函数都包含在

     memset():C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

    • str -- 指向要填充的内存块。
    • c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
    • n -- 要被设置为该值的字符数。

    memcpy():C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。

    • str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
    • str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
    • n -- 要被复制的字节数。

     分析:层序遍历即利用队列先进先出的特点,现将根节点入队,若队列不空则将根节点出队,然后将根节点的左右孩子入队再判断队列空不空,若不空则出队一个元素,入队其左右孩子,依次循环~

    如图所示,左边一颗二叉树,AB表示已经出队,队列里面元素为CDE

    1. //层次遍历
    2. void LevelOrderBinTree(BinTree T)
    3. {
    4. Queue Q;
    5. ElemType e=T;
    6. InitQueue(&Q);
    7. EnQueue(&Q,&e);
    8. while(!IsEmpty(&Q))
    9. {
    10. DeQueue(&Q,&e);
    11. visit(e);
    12. if(e->lchild) EnQueue(&Q,&e->lchild);
    13. if(e->rchild) EnQueue(&Q,&e->rchild);
    14. }
    15. }

    知道了这个原理我们就可以求一个二叉树的最大宽度了~

    • 二叉宽度 

    1. //宽度
    2. int WidthBinTree(BinTree T)
    3. {
    4. if(!T) return 0;//空树
    5. Queue Q;
    6. ElemType e=T;
    7. int temp=0,last=0,max=0;//last为一层中最后一个结点在队列的位置,
    8. InitQueue(&Q);
    9. EnQueue(&Q,&e);
    10. while(Q.front <= last){//每出队一个元素front+1
    11. DeQueue(&Q, &e);
    12. temp++;//temp为树每层的结点个数
    13. if (e->lchild != NULL)//每次入队rear+1
    14. EnQueue(&Q, &e->lchild);
    15. if (e->rchild != NULL)
    16. EnQueue(&Q, &e->rchild);
    17. if (Q.front > last){//每一层结束后更新
    18. last = Q.rear-1;
    19. max = max > temp ? max : temp;
    20. temp = 0;
    21. }
    22. }
    23. return max;
    24. }

     按照层序遍历的思路,我们将根节点入队,然后出队,temp表示二叉树每层的结点,每次出队一个元素temp++,然后把根节点的左右孩子入队,判断front指针是否大于last(每层结点最后元素的位置),即判断这一层的元素是否全部出队(全部出队则front与上一次的rear相等,即大于last(rear-1)),如果全部出队则更新last,max(最大宽度)和temp,最后递归所有元素然后返回最大宽度max。

    • 完整代码 

    1. #include
    2. #include
    3. #include
    4. #define MAXSIZE 100
    5. //树的结构体
    6. typedef struct BinNode{
    7. char data;
    8. struct BinNode *lchild,*rchild;
    9. }BinNode,*BinTree;
    10. typedef BinTree ElemType;
    11. //队
    12. typedef struct Queue{
    13. ElemType data[MAXSIZE];
    14. int front,rear;
    15. }Queue,*PtrQueue;
    16. //初始化队列
    17. void InitQueue(PtrQueue Q)
    18. {
    19. Q->front=Q->rear=0;
    20. memset(Q->data,0,sizeof(ElemType)*MAXSIZE);
    21. }
    22. //判满
    23. int IsFull(PtrQueue Q)
    24. {
    25. if ((Q->rear+1) % MAXSIZE == Q->front) return 1;
    26. return 0;
    27. }
    28. //判空
    29. int IsEmpty(PtrQueue Q)
    30. {
    31. if (Q->rear == Q->front) return 1;
    32. return 0;
    33. }
    34. //入队
    35. void EnQueue(PtrQueue Q,ElemType *e)
    36. {
    37. if(!Q || !e) return;//队为空或者树结点为空
    38. if(!IsFull(Q))
    39. {
    40. memcpy(&Q->data[Q->rear],e,sizeof(ElemType));
    41. Q->rear=(Q->rear+1)%MAXSIZE;
    42. }
    43. }
    44. //出队
    45. void DeQueue(PtrQueue Q,ElemType *e)
    46. {
    47. if(!Q) return;
    48. if(!IsEmpty(Q))
    49. {
    50. memcpy(e,&Q->data[Q->front],sizeof(ElemType));
    51. Q->front=(Q->front+1)%MAXSIZE;
    52. }
    53. }
    54. //先序创建树
    55. void CreateBinTree(BinTree *T)
    56. {
    57. char ch;
    58. if((ch=getchar())=='#') *T=NULL;
    59. else
    60. {
    61. (*T)=(BinTree)malloc(sizeof(BinNode));
    62. (*T)->data=ch;
    63. CreateBinTree((BinTree *)&((*T)->lchild));//强制类型转换 (BinTree *)
    64. CreateBinTree((BinTree *)&((*T)->rchild));
    65. }
    66. printf("创建成功!\n");
    67. }
    68. void visit(BinTree T)
    69. {
    70. printf("%c",T->data);
    71. }
    72. //层次遍历
    73. void LevelOrderBinTree(BinTree T)
    74. {
    75. Queue Q;
    76. ElemType e=T;
    77. InitQueue(&Q);
    78. EnQueue(&Q,&e);
    79. while(!IsEmpty(&Q))
    80. {
    81. DeQueue(&Q,&e);
    82. visit(e);
    83. if(e->lchild) EnQueue(&Q,&e->lchild);
    84. if(e->rchild) EnQueue(&Q,&e->rchild);
    85. }
    86. }
    87. //宽度
    88. int WidthBinTree(BinTree T)
    89. {
    90. if(!T) return 0;//空树
    91. Queue Q;
    92. ElemType e=T;
    93. int temp=0,last=0,max=0;//last为一层中最后一个结点在队列的位置,
    94. InitQueue(&Q);
    95. EnQueue(&Q,&e);
    96. while(Q.front <= last){//每出队一个元素front+1
    97. DeQueue(&Q, &e);
    98. temp++;//temp为树每层的结点个数
    99. if (e->lchild != NULL)//每次入队rear+1
    100. EnQueue(&Q, &e->lchild);
    101. if (e->rchild != NULL)
    102. EnQueue(&Q, &e->rchild);
    103. if (Q.front > last){//每一层结束后更新
    104. last = Q.rear-1;
    105. max = max > temp ? max : temp;
    106. temp = 0;
    107. }
    108. }
    109. return max;
    110. }
    111. int main(){
    112. BinTree T = NULL;
    113. int width;
    114. printf("请构造一棵二叉树(#表示空):\n");
    115. CreateBinTree(&T);
    116. printf("层次遍历序列:\n");
    117. LevelOrderBinTree(T);
    118. putchar('\n');
    119. width = WidthBinTree(T);
    120. printf("树的最大宽度为:%d\n", width);
    121. return 0;
    122. }

  • 相关阅读:
    隐函数求导例题
    ECMAScript 6 Reflect / Proxy
    国内首家获批元宇宙行业协会揭牌,关注三大受益方向
    【数据结构】队列实现+层序遍历详解+一些练题
    cmd路径名太长,缩短路径方法
    AVL树节点插入方式解析(单旋转和双旋转)
    发送消息(二)RoutingKafkaTemplate,DefaultKafkaProducerFactory和 ReplyingKafkaTemplate
    TiDB Lightning 简介
    Vue2监听浏览器可视区域的大小,
    【LeetCode】58. 最后一个单词的长度
  • 原文地址:https://blog.csdn.net/m0_62858660/article/details/127649458