数据布局:树形布局(树、堆)详解

打印 上一主题 下一主题

主题 1598|帖子 1598|积分 4794

一、树

树的物理布局和逻辑布局上都是树形布局
(一)树的性子

   • ⼦树是不相交的
• 除了根结点外,每个结点有且仅有⼀个⽗结点
• ⼀棵N个结点的树有N-1条边
  (二)树的种类

树按照根节点的分支来分,可以分为二叉树和多叉树。
二叉树

   二叉树(Binary Tree)
界说:每个节点最多有两个子节点的树布局。可以是空树,或者由一个根节点和左、右子树组成。
  多叉树

   多叉树(Multiway Tree)
界说:每个节点可以有多个子节点的树布局,节点子节点的数量没有限定。
  树按照结点的特性来观察,又可以有满N叉树和完全N叉树
满N叉树

   满N叉树是一种深度为K的二叉树,此中每一层的节点数都到达了该层能有的最大节点数。
  

完全N叉树

   除了最后一层外,每一层都被完全填满,而且最后一层所有节点都尽量靠左排列。
  

(三)二叉树的实现

1、二叉树布局界说

用 typedef 可以使得反面的利用范围更广
  1. typedef int BTDataType;
  2. typedef struct BinaryTreeNode
  3. {
  4.         BTDataType data;
  5.         struct BinaryTreeNode* left;
  6.         struct BinaryTreeNode* right;
  7. }BTNode;
复制代码
2、二叉树功能实现

(1)前序、中序、后序、层序遍历

下面的层序遍历方式采用的是一层一层的处理方式
  1. void PreOrder(BTNode* root) {
  2.         if (root == NULL) return;
  3.         printf("%d ", root->data);
  4.         PreOrder(root->left);
  5.         PreOrder(root->right);
  6.         return;
  7. }
  8. void InOrder(BTNode* root) {
  9.         if (root == NULL) return;
  10.         InOrder(root->left);
  11.         printf("%d ", root->data);
  12.         InOrder(root->right);
  13.         return;
  14. }
  15. void PostOrder(BTNode* root) {
  16.         if (root == NULL) return;
  17.         PostOrder(root->left);
  18.         PostOrder(root->right);
  19.         printf("%d ", root->data);
  20.         return;
  21. }
  22. void LevelOrder(BTNode* root) {
  23.         queue<BTNode*> q;
  24.         q.push(root);
  25.         while (!q.empty()) {
  26.                 int num = q.size();
  27.                 for (int i = 0; i < num; i++) {
  28.                         BTNode* temp = q.front();
  29.                         if(temp->left) q.push(temp->left);
  30.                         if(temp->right) q.push(temp->right);
  31.                         printf("%d ", temp->data);
  32.                         q.pop();
  33.                 }
  34.                 printf("\n");
  35.         }
  36.         return;
  37. }
复制代码
(2)二叉树结点个数

两种方法都可以实现求结点个数,但是第二种必要别的创建变量吸收返回值,因此第一种方式比力好
  1. //方法一
  2. int BinaryTreeSize(BTNode* root) {
  3.         if (root == NULL) return 0;
  4.         return 1 + BinaryTreeSize(root->left) +
  5.          BinaryTreeSize(root->right);
  6. }
  7. //方法二
  8. void BinaryTreeSize(BTNode* root, int* psize) {
  9.         if (root == NULL) return;
  10.         if (root->left) {
  11.                 (*psize)++;
  12.                 BinaryTreeSize(root->left, psize);
  13.         }
  14.         if (root->right) {
  15.                 (*psize)++;
  16.                 BinaryTreeSize(root->right, psize);
  17.         }
  18.         return;
  19. }
复制代码
(3) ⼆叉树叶⼦结点个数

只必要统计叶子结点即可,和求普通结点个数相似
  1. int BinaryTreeLeafSize(BTNode* root) {
  2.         if (root == NULL) return 0;
  3.         if (root->left == NULL && root->right == NULL) return 1;
  4.         return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
  5. }
复制代码
(4) 二叉树第k层结点个数

必要加一个二叉树层数的变量
  1. int BinaryTreeLevelKSize(BTNode* root, int k) {
  2.         if (root == NULL) return 0;
  3.         if (k == 1) return 1;
  4.         return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
  5. }
复制代码
(5)二叉树的深度/高度

  1. int BinaryTreeDepth(BTNode* root) {
  2.         if (root == NULL) return 0;
  3.         int a = BinaryTreeDepth(root->left);
  4.         int b = BinaryTreeDepth(root->right);
  5.         return (a > b ? a : b) + 1;
  6. }
复制代码
(6)⼆叉树查找值为x的结点

如果没有找到,不要忘记返回空
  1. BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
  2.         if (root == NULL) return NULL;
  3.         if (root->data == x) return root;
  4.         BTNode* left = BinaryTreeFind(root->left, x);
  5.         if (left) return left;
  6.         BTNode* right = BinaryTreeFind(root->right, x);
  7.         if (right) return right;
  8.         return NULL;
  9. }
复制代码
(7)二叉树销毁

采用二级指针的方式传入,可以制止函数处理后在举行置空处理。
  1. void BinaryTreeDestory(BTNode** root) {
  2.         if (*root == NULL) return;
  3.         BinaryTreeDestory(&((*root)->left));
  4.         BinaryTreeDestory(&((*root)->right));
  5.         free(*root);
  6.         *root = NULL;
  7.         return;
  8. }
复制代码
(8)判断二叉树是否为完全二叉树

这段代码是老夫目前想了许久,以为很有不错的代码,先不思量它的实现复杂度以及简洁水平,它实现的功能不错,可以将二叉树包括空结点放在队列之中,自己以为很好,哈哈,也许你没看到这句,那我就放心了。
  1. bool BinaryTreeComplete(BTNode* root) {
  2.         queue<BTNode*> q;
  3.         BinaryTreePushQueue(root, q);
  4.         while (!q.empty()) {
  5.                 if (q.front() != NULL) q.pop();
  6.                 else break;
  7.         }
  8.         while (!q.empty()) {
  9.                 if (q.front() == NULL) q.pop();
  10.                 else return false;
  11.         }
  12.         return true;
  13. }
  14. void BinaryTreePushQueue(BTNode* root, queue<BTNode*>& q) {
  15.         vector<vector<BTNode*>> v;
  16.         BinaryNodePushVector(root, v, 0);
  17.         for (int i = 0; i < v.size(); i++) {
  18.                 for (auto x : v[i]) {
  19.                         q.push(x);
  20.                 }
  21.         }
  22.         return;
  23. }
  24. void BinaryNodePushVector(BTNode* root, vector<vector<BTNode*>>& v, int k) {
  25.         if (v.size() == k) v.push_back(vector<BTNode*>());
  26.         if (root == NULL) {
  27.                 v[k].push_back(NULL);  //如果不处理空结点,取消这步即可
  28.                 return;
  29.         }
  30.         v[k].push_back(root);
  31.         BinaryNodePushVector(root->left, v, k + 1);
  32.         BinaryNodePushVector(root->right, v, k + 1);
  33.         return;
  34. }
复制代码
二、堆

堆的物理布局是一段连续空间,但是逻辑机构是树形布局
(一)堆的实现

1、堆的布局界说

下面通过宏函数来实现交换,可以使得交换简便,或者用指针也行。
  1. typedef int HeapDataType;
  2. typedef struct Heap {
  3.         HeapDataType* __data;
  4.         HeapDataType* data;
  5.         int count;
  6.         int capacity;
  7. }Heap;
复制代码
  1. #define SWAP(a ,b){\
  2.         HeapDataType c = (a);\
  3.         (a) = (b);\
  4.         (b) = (c);\
  5. }
复制代码
2、堆的初始化

用偏移量的方式,节约了内存。
从数组下标为1开始分配结点,使得反面求父节点,左右孩子运算和逻辑更简单
  1. void HeapInit(Heap* pHeap) {
  2.         assert(pHeap);
  3.         pHeap->__data = (HeapDataType*)malloc(sizeof(HeapDataType));
  4.         pHeap->data = pHeap->__data - 1;
  5.         pHeap->capacity = 1;
  6.         pHeap->count = 0;
  7.         return;
  8. }
复制代码
3、向上调整操作

可以利用递归或者是循环来实现向上调整
  1. void Heap_UP_Update(Heap* pHeap, int i) {
  2.         assert(pHeap);
  3.         while (FATHER(i) >= 1 && pHeap->data[FATHER(i)] > pHeap->data[i]) {
  4.                 SWAP(pHeap->data[FATHER(i)], pHeap->data[i]);
  5.                 i = FATHER(i);
  6.         }
  7.         return;
  8. }
  9. void DG_Heap_UP_Update(Heap* pHeap, int i) {
  10.         assert(pHeap);
  11.         if (FATHER(i) < 1) return;
  12.         if (pHeap->data[FATHER(i)] > pHeap->data[i]) {
  13.                 SWAP(pHeap->data[FATHER(i)], pHeap->data[i]);
  14.                 i = FATHER(i);
  15.                 DG_Heap_UP_Update(pHeap, i);
  16.         }
  17.         return;
  18. }
复制代码
4、向下调整操作

  1. void Heap_DOWN_Update(Heap* pHeap, int i) {
  2.         assert(pHeap);
  3.         int size = pHeap->count - 1;
  4.         while (LEFT(i) <= size) {
  5.                 int l = LEFT(i), r = RIGHT(i), ind = i;
  6.                 if (pHeap->data[ind] > pHeap->data[l]) ind = l;
  7.                 if (r <= size && pHeap->data[ind] > pHeap->data[r]) ind = r;
  8.                 if (ind == i) break;
  9.                 SWAP(pHeap->data[i], pHeap->data[ind]);
  10.                 i = ind;
  11.         }
  12.         return;
  13. }
复制代码
5、入堆操作

  1. void HeapPush(Heap* pHeap, HeapDataType x) {
  2.         assert(pHeap);
  3.         HeapCheckCapacity(pHeap);
  4.         pHeap->data[pHeap->count + 1] = x;
  5.         DG_Heap_UP_Update(pHeap, pHeap->count + 1);
  6.         pHeap->count += 1;
  7.         return;
  8. }
复制代码
6、堆的扩容

  1. void HeapCheckCapacity(Heap* pHeap) {
  2.         assert(pHeap);
  3.         if (pHeap->capacity == pHeap->count) {
  4.                 HeapDataType* temp = (HeapDataType*)realloc(pHeap->__data, 2 * pHeap->capacity * sizeof(HeapDataType));
  5.                 if (!temp) {
  6.                         perror("Heap Realloc Fail!");
  7.                         exit(1);
  8.                 }
  9.                 pHeap->__data = temp;
  10.                 pHeap->capacity *= 2;
  11.         }
  12.         return;
  13. }
复制代码
7、出堆操作

  1. void HeapPop(Heap* pHeap) {
  2.         assert(pHeap);
  3.         assert(!HeapEmpty(pHeap));
  4.         pHeap->data[1] = pHeap->data[pHeap->count];
  5.         Heap_DOWN_Update(pHeap, 1);
  6.         pHeap->count -= 1;
  7.         return;
  8. }
复制代码
8、堆的销毁

  1. void HeapDestroy(Heap* pHeap) {
  2.         assert(pHeap);
  3.         free(pHeap->__data);
  4.         pHeap->data = NULL;
  5.         pHeap->__data = NULL;
  6.         pHeap->capacity = 0;
  7.         pHeap->count = 0;
  8.         return;
  9. }
复制代码
9、堆的判空、检察堆顶元素

  1. int HeapEmpty(Heap* pHeap) {
  2.         assert(pHeap);
  3.         return pHeap->count == 0;
  4. }
  5. HeapDataType HeapTop(Heap* pHeap) {
  6.         assert(!HeapEmpty(pHeap));
  7.         return pHeap->data[1];
  8. }
复制代码
(二)哈夫曼编码实现

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<time.h>
  5. #include<string.h>
  6. #include<algorithm>
  7. #include<unordered_map>
  8. #include<vector>
  9. using namespace std;
  10. #define FATHER(i) ((i) / 2)
  11. #define LEFT(i) ((i) * 2)
  12. #define RIGHT(i) ((i) * 2 + 1)
  13. typedef struct Node {
  14.         char* c;
  15.         int freq;
  16.         struct Node* lchild, * rchild;
  17. }Node;
  18. template<typename T>
  19. void Swap(T& a, T& b) {
  20.         T c = a;
  21.         a = b;
  22.         b = c;
  23.         return;
  24. }
  25. //void swap(Node* a, Node* b) {
  26. //        Node* c = a;
  27. //        a = b;
  28. //        b = c;
  29. //        return;
  30. //}
  31. Node* getNewNode(int freq,const char* c) {
  32.         Node* p = (Node*)malloc(sizeof(Node));
  33.         p->freq = freq;
  34.         p->c = _strdup(c);
  35.         p->lchild = p->rchild = NULL;
  36.         return p;
  37. }
  38. void clear(Node* root) {
  39.         if (root == NULL) return;
  40.         clear(root->lchild);
  41.         clear(root->rchild);
  42.         free(root);
  43.         return;
  44. }
  45. typedef struct heap {
  46.         Node** data, **__data;
  47.         int size, count;
  48. }heap;
  49. heap* initHeap(int n) {
  50.         heap* p = (heap*)malloc(sizeof(heap));
  51.         p->__data = (Node**)malloc(sizeof(Node*) * n);
  52.         p->data = p->__data - 1;
  53.         p->size = n;
  54.         p->count = 0;
  55.         return p;
  56. }
  57. int empty(heap* h) {
  58.         return h->count == 0;
  59. }
  60. int full(heap* h) {
  61.         return h->count == h->size;
  62. }
  63. Node* top(heap* h) {
  64.         if (empty(h)) return NULL;
  65.         return h->data[1];
  66. }
  67. //void up_update(Node** data, int i) {
  68. //        while (FATHER(i) >= 1) {
  69. //                int ind = i;
  70. //                if (data[i]->freq < data[FATHER(i)]->freq) {
  71. //                        swap(data[i], data[FATHER(i)]);
  72. //                }
  73. //                if (ind == i) break;
  74. //                i = FATHER(i);
  75. //        }
  76. //        return;
  77. //}
  78. void up_update(Node** data, int i) {
  79.         while (i > 1 && data[i]->freq < data[FATHER(i)]->freq) {
  80.                 Swap(data[i], data[FATHER(i)]);
  81.                 i = FATHER(i);
  82.         }
  83.         return;
  84. }
  85. void down_update(Node** data, int i, int n) {
  86.         while (LEFT(i) <= n) {
  87.                 int ind = i, l = LEFT(i), r = RIGHT(i);
  88.                 if (data[i]->freq > data[l]->freq) ind = l;
  89.                 if (RIGHT(i) <= n && data[r]->freq < data[ind]->freq) ind = r;
  90.                 if (ind == i) break;
  91.                 Swap(data[ind], data[i]);
  92.                 i = ind;
  93.         }
  94.         return;
  95. }
  96. void push(heap* h, Node* node) {
  97.         if (full(h)) return;
  98.         h->count += 1;
  99.         h->data[h->count] = node;
  100.         up_update(h->data, h->count);
  101.         return;
  102. }
  103. void pop(heap* h) {
  104.         if (empty(h)) return;
  105.         h->data[1] = h->data[h->count];
  106.         h->count -= 1;
  107.         down_update(h->data, 1, h->count);
  108.         return;
  109. }
  110. void clearHeap(heap* h) {
  111.         if (h == NULL) return;
  112.         free(h->__data);
  113.         free(h);
  114.         return;
  115. }
  116. Node* build_huffman_tree(Node** nodeArr, int n) {
  117.         heap* h = initHeap(n);
  118.         for (int i = 0; i < n; i++) {
  119.                 push(h, nodeArr[i]);
  120.         }
  121.         Node* node1, * node2;
  122.         int freq;
  123.         for (int i = 1; i < n; i++) {
  124.                 node1 = top(h);
  125.                 pop(h);
  126.                 node2 = top(h);
  127.                 pop(h);
  128.                 freq = node1->freq + node2->freq;
  129.                 Node* node3 = getNewNode(freq, "0");
  130.                 node3->lchild = node1;
  131.                 node3->rchild = node2;
  132.                 push(h, node3);
  133.         }
  134.         return h->data[1];
  135. }
  136. void output(Node* root) {
  137.         if (root == NULL) return;
  138.         output(root->lchild);
  139.         //if (root->lchild == NULL && root->rchild == NULL)
  140.                 printf("%s : %d\n", root->c, root->freq);
  141.         output(root->rchild);
  142.         return;
  143. }
  144. char charArr[100];
  145. unordered_map<char*, char*> h;
  146. void extract_huffman_code(Node* root, int i) {
  147.         charArr[i] = 0;
  148.         if (root->lchild == NULL && root->rchild == NULL) {
  149.                 h[root->c] = _strdup(charArr);
  150.                 return;
  151.         }
  152.         charArr[i - 1] = '0';
  153.         extract_huffman_code(root->lchild, i + 1);
  154.         charArr[i - 1] = '1';
  155.         extract_huffman_code(root->rchild, i + 1);
  156.         return;
  157. }
  158. int main() {
  159. #define MAX_CHAR 26
  160.         //1.首先用一个数组读取相关字符串及其频率
  161.         Node** charArr = (Node**)malloc(sizeof(Node*)*MAX_CHAR);
  162.         char arr[10];
  163.         int freq;
  164.         for (int i = 0; i < MAX_CHAR;i++) {
  165.                 scanf("%s%d", arr, &freq);
  166.                 charArr[i] = getNewNode(freq, arr);
  167.         }
  168.         //2.建立哈夫曼树
  169.         Node* root = build_huffman_tree(charArr, MAX_CHAR);
  170.         //3.提取哈夫曼编码进入unordered_map
  171.         extract_huffman_code(root, 1);
  172.         //4.将unordered_map转换成vector排序,可以按照字典序输出编码
  173.         vector<pair<char*, char*>> v(h.begin(), h.end());
  174.         sort(v.begin(), v.end(), [&](const pair<char*, char*>& a, const pair<char*, char*>& b) {
  175.                 return strcmp(a.first, b.first) < 0;
  176.                 });
  177.         for (auto& x : v) {
  178.                 printf("%s : %s\n", x.first, x.second);
  179.         }
  180.         return 0;
  181. }
复制代码

结束语

关注博主的专栏,博主会分享更多的数据布局知识!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表