当前位置:   article > 正文

嵌入式学习一阶段——C语言:数据结构(6)_若干棵树的森林转换的二叉树,坚持左孩子右兄弟原则,结点左指针指向第一个孩

若干棵树的森林转换的二叉树,坚持左孩子右兄弟原则,结点左指针指向第一个孩

树和森林

常用的数据结构

双亲表示法

这种存储方式采用一组连续的空间来存储每个结点,同时每一个结点中又设置了一个伪指针,指示其双亲结点在数组中的位置!!  规定根节点下标为0 , 伪指针为-1;
如图:

孩子表示法

孩子表示法是将每一个基点的孩子结点都用单链表链接起来形成一个线性结构,此时n个结点就会有n个孩子链表(叶子结点的孩子链表为空)
这个存储方式,找儿子贼方便
如图:

孩子兄弟表示法

孩子兄弟表示法又称之为二叉树表示法!即以二叉链表作为树的存储结构。孩子兄弟表示法使每一个结点包括三部分: 1、节点值 2、指向结点第一个孩子结点的指针 3、以及指向结点下一个兄弟结点的指针(根据这个指针就可以找到该结点所有兄弟) 如图:

树转换成二叉树

树转换成二叉树的规则:每一个结点左指针指向它第一个孩子,右指针指向它在树中的相邻右兄弟,这个规则:“左孩子右兄弟法”

森林转换成二叉树

森林是由若干棵树组成,所有我们可以完全理解为,森林中每一颗树都是兄弟!可以按照兄弟的处理方式来操作,操作如下:
    1、把每颗树先转换成二叉树
    2、第一颗树不动,从第二棵二叉树开始,依次把后一棵二叉树的根节点作为前一棵树的根节点右孩子,用线连起来。
        当所有的二叉树连接起来后得到由森林转换而来的二叉树!!

二叉树转成树

如图:

平衡二叉树

单向右旋

单向左旋

先右旋再左旋

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include"avltree.h"
  4. #define Max(a,b) ((a)>(b)?(a):(b))
  5. //单向右旋
  6. Binode* Single_right(Binode* k2)
  7. {
  8. Binode* k1=k2->lchild;
  9. k2->lchild=k1->rchild;
  10. k1->rchild=k2;
  11. k2->h=Max(Height(k2->lchild),Height(k2->rchild))+1;
  12. k1->h=Max(Height(k1->lchild),Height(k1->rchild))+1;
  13. return k1;
  14. }
  15. //单向左旋
  16. Binode* Single_left(Binode* k2)
  17. {
  18. Binode* k1=k2->rchild;
  19. k2->rchild=k1->lchild;
  20. k1->lchild=k2;
  21. k2->h=Max(Height(k2->lchild),Height(k2->rchild))+1;
  22. k1->h=Max(Height(k1->lchild),Height(k1->rchild))+1;
  23. return k1;
  24. }
  25. //先右旋再左旋
  26. Binode* Single_rightleft(Binode* k2)
  27. {
  28. k2->rchild=Single_right(k2->rchild);
  29. return Single_left(k2);
  30. }
  31. //先左旋再右旋
  32. Binode* Single_leftright(Binode* k2)
  33. {
  34. k2->lchild=Single_left(k2->lchild);
  35. return Single_right(k2);
  36. }
  37. //返回高度
  38. int Height(Binode* root)
  39. {
  40. if(root==NULL)
  41. return 0;
  42. else
  43. return root->h;
  44. }
  45. //创建平衡二叉树
  46. Binode* create_avltree(void)
  47. {
  48. Binode* root=NULL;
  49. int d;
  50. while(1)
  51. {
  52. scanf("%d",&d);
  53. if(d==0)
  54. {
  55. break;
  56. }
  57. Binode* p=(Binode*)malloc(sizeof(Binode));
  58. p->data=d;
  59. p->lchild=NULL;
  60. p->rchild=NULL;
  61. p->h=1;
  62. root=insert(root,p);
  63. }
  64. return root;
  65. }
  66. //插入
  67. Binode* insert(Binode* root,Binode* p)
  68. {
  69. if(root==NULL)
  70. {
  71. return p;
  72. }
  73. if(p->data < root->data)
  74. {
  75. root->lchild=insert(root->lchild,p);
  76. root->h=Max(Height(root->lchild),Height(root->rchild))+1;
  77. if(abs(Height(root->lchild)-Height(root->rchild))>1)
  78. {
  79. if(p->data<root->lchild->data)//左边的左边添加,导致不平衡
  80. {
  81. //右旋
  82. root=Single_right(root);
  83. }
  84. else//左边的右边添加,导致不平衡
  85. {
  86. //先左旋再右旋
  87. root=Single_leftright(root);
  88. }
  89. }
  90. }
  91. else
  92. {
  93. root->rchild=insert(root->rchild,p);
  94. root->h=Max(Height(root->lchild),Height(root->rchild))+1;
  95. if(abs(Height(root->lchild)-Height(root->rchild))>1)
  96. {
  97. if(p->data>root->rchild->data)//在右边的右边添加,导致不平衡
  98. {
  99. //左旋
  100. root=Single_left(root);
  101. }
  102. else//在右边的左边添加,导致不平衡
  103. {
  104. //先右旋再左旋
  105. root=Single_rightleft(root);
  106. }
  107. }
  108. }
  109. return root;
  110. }
  111. /**
  112. * @description: 先序遍历
  113. * @param {Binode} *root
  114. * @return {*}
  115. */
  116. void pre_print(Binode *root)
  117. {
  118. if(root==NULL)
  119. {
  120. return;
  121. }
  122. printf("%d ",root->data);
  123. pre_print(root->lchild);
  124. pre_print(root->rchild);
  125. }

哈夫曼树

哈夫曼树

在许多的应用中,树中的结点常常被赋予一个标识某种意义的数值,称为这个结点的权。从树的根到任意结点的路径长度(经过边数)与该节点上权值的乘积,称为该结点的带权路径长度。树种的所有的叶子结点的带权路径之和称为该树的带权路径长度,记为WPL。
​
在含有 n 个带权叶子结点的二叉树中,其中带权路径长度(wpl)最小的二叉树称为 哈夫曼树,也称之为最优二叉树 

经过验证,C就是哈夫曼树

如何构造一棵哈夫曼树

从上看出哈夫曼树的特点 (1)、每一个初始节点最终会变成叶子节点,并且权值越小的节点到根节点的路径长度越大 (2)、构造的过程中共创建n-1个新节点(双分支节点),因此哈夫曼树的节点总数2n-1 (3)、每次构造的时候都选择了两颗树作为新节点孩子,所以哈弗满树没有度为1 的节点!!

哈夫曼编码

在电报通信中,电文是以二进制0/1序列传送的,每个字符对应一个二进制编码。
为了缩短电文的总长度,采用不等长编码方式,把使用频率较高的字符用短编码,
使用频率低的用长编码。
​
我们把使用频率作为权值,每个字符作为叶子结点来构建哈夫曼树,然后每个分支结点
的左右分支分别用0和1编码,这样就得到了每个叶子结点的二进制编码。
​
因为使用的是叶子结点的编码,所以每个字符的编码都不可能是其他字符编码的前缀。

哈夫曼树的伪代码

struct Hnode { char data; struct Hnode *lchild; struct Hnode *rchild; int weight; };

//假设结点数不超过30 (只用5棵树) struct Hnode * h[30] , root;

for(i = 0 ; i< 30 ; i++) { h[i] = malloc(sizeof(struct Hnode)); }

char ch[] = {'A','B','C','D','E'}; int weight[] = {2,9,5,7,8};

//为每单根树进行初始化 for(i = 0 ; i< 5 ; i++) { h[i]->data = ch[i]; h[i]->weight = weight[i]; h[i]->lchild = NULL; h[i]->rchild = NULL; }

while(n>1) { //排序,从大到小 sort(h,n); struct Hnode *s = malloc(sizeof(struct Hnode *)); s->data = ''; s->lchild = h[n-1];//小的 s->rchild = h[n-2];//大的 s->weight = h[n-1]->weight + h[n-2]->weight; h[n-2] = s; n--; }

  1. #include "HuffmanTree.h"
  2. #include "PriorityQueue.h"
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. /*根据键盘输入,创建一棵哈夫曼树*/
  6. HTNode *create_Huffman()
  7. {
  8. HTNode *r=NULL; //哈夫曼树的根结点
  9. PriorityQueue * pq = InitQueue(); //pq 队列
  10. /*step1: 构建森林*/
  11. while(1)
  12. {
  13. int d;
  14. scanf("%d",&d);
  15. if(d == -1)
  16. break;
  17. HTNode *pnew = malloc(sizeof(*pnew));
  18. pnew->weight = d;
  19. pnew->left = NULL;
  20. pnew->right = NULL;
  21. EnQueue( pq, pnew);//把创建的树入队
  22. }
  23. struct node *p= pq->front;
  24. while(p)
  25. {
  26. printf("%d ",p->data->weight);
  27. p = p->next;
  28. }
  29. putchar('\n');
  30. while(QueueLen(pq) >= 2)
  31. {
  32. /*step2: 从优先队列取出两个结点,并创建父节点*/
  33. HTNode *p1 = DeQueue(pq);
  34. HTNode *p2 = DeQueue(pq);
  35. printf("p1->%d\n",p1->weight);
  36. printf("p1->%d\n",p2->weight);
  37. HTNode *parent = malloc(sizeof(*parent));
  38. parent->left = p1;
  39. parent->right = p2;
  40. parent->weight = p1->weight + p2->weight;
  41. /*step3: 把父节点入队*/
  42. EnQueue(pq, parent);
  43. }
  44. r = DeQueue(pq);//最后出队,把该结点作为根结点
  45. DestroyQueue(pq);
  46. return r;
  47. }
  48. void first_order(HTNode *r)
  49. {
  50. if(r == NULL)
  51. return;
  52. /*step1: 访问根结点*/
  53. printf("%d ",r->weight);
  54. /*step2: 用先序遍历的规则访问左子树*/
  55. first_order(r->left);
  56. /*step3: 用先序遍历的规则访问右子树*/
  57. first_order(r->right);
  58. }
  59. void middle_order(HTNode *r)
  60. {
  61. if(r == NULL)
  62. return;
  63. /*step1: 用中序遍历的规则访问左子树*/
  64. middle_order(r->left);
  65. /*step2: 访问根结点*/
  66. printf("%d ",r->weight);
  67. /*step3: 用中序遍历的规则访问右子树*/
  68. middle_order(r->right);
  69. }
  70. void last_order(HTNode *r)
  71. {
  72. if(r == NULL)
  73. return;
  74. /*step1: 用后序遍历的规则访问左子树*/
  75. last_order(r->left);
  76. /*step2: 用后序遍历的规则访问右子树*/
  77. last_order(r->right);
  78. /*step3: 访问根结点*/
  79. printf("%d ",r->weight);
  80. }
  81. void HuffmanCoding(HTNode *r, int len)
  82. {
  83. static int Coding[1024];
  84. if(r == NULL)
  85. return;
  86. if(r->left==NULL && r->right==NULL)//r就是叶子结点
  87. {
  88. printf("%d Coding: ",r->weight);
  89. for(int i=0;i<len;i++)
  90. {
  91. printf("%d",Coding[i]);
  92. }
  93. putchar('\n');
  94. }
  95. else
  96. {
  97. Coding[len] = 0;
  98. HuffmanCoding(r->left, len+1);
  99. Coding[len] = 1;
  100. HuffmanCoding(r->right, len+1);
  101. }
  102. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/692679
推荐阅读
相关标签
  

闽ICP备14008679号