数据结构学习第十五天
Posted 57one
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构学习第十五天相关的知识,希望对你有一定的参考价值。
11:36:53 2019-08-30
学习
带权路径长度(WPL) (Weighted Path Length of Tree),设二叉树有n个叶子节点,每个叶子节点带有权值$w_k$,从根节点到每个叶子节点的长度为$l_k$,则每个叶子节点的带权路径长度之和就是:$WPL=\displaystyle \sum_k=1^nw_kl_k$
最优二叉树或哈夫曼树:WPL最小的二叉树
哈夫曼树的构造:每次把权值最小的两课二叉树合并
利用最小堆 实现哈夫曼树:
1 #include<stdio.h> 2 #include<malloc.h> 3 4 typedef struct TreeNode* HuffmanTree; 5 struct TreeNode 6 7 int Weight; //权重 8 HuffmanTree Right, Left; 9 ; 10 typedef struct HeapStruct* MinHeap; 11 struct HeapStruct 12 13 HuffmanTree* Elements; 14 int Capacity; 15 int Size; 16 ; 17 18 MinHeap Create(int MaxSize); //创造一个空的最小堆 19 int IsFull(MinHeap H); 20 void Insert(MinHeap H, HuffmanTree T); 21 HuffmanTree DeleteMin(MinHeap H); //删除一个权重最小的节点 并返回 22 23 MinHeap Create(int MaxSize) 24 25 MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct)); 26 H->Elements = (HuffmanTree*)malloc(sizeof(struct TreeNode) * (MaxSize + 1)); 27 H->Capacity = MaxSize; 28 H->Size = 0; 29 H->Elements[0]->Weight = -1; //设置哨兵 30 return H; 31 32 int IsFull(MinHeap H) 33 34 return (H->Size == H->Capacity) ? 1 : 0; 35 36 void Insert(MinHeap H, HuffmanTree T) 37 38 if (IsFull(H)) 39 return; 40 int i = ++H->Size; 41 for (; T->Weight < H->Elements[i / 2]->Weight; i /= 2) 42 H->Elements[i] = H->Elements[i / 2]; 43 H->Elements[i] = T; 44 45 HuffmanTree DeleteMin(MinHeap H) 46 47 if (IsFull(H)) 48 return; 49 HuffmanTree MinItem = H->Elements[1]; 50 HuffmanTree Tmp = H->Elements[H->Size--]; 51 int Parent, Child; //这俩个记录的是位置 52 for (Parent = 1; Parent * 2 <= H->Size; Parent = Child) 53 54 Child = Parent * 2; 55 if (Child != H->Size && H->Elements[Child]->Weight > H->Elements[Child + 1]->Weight) 56 Child++; 57 if (Tmp->Weight <= H->Elements[Child]->Weight)break; //注意这边 找到后就立即结束循环 这样才能添加到正确位置 58 else 59 H->Elements[Parent] = H->Elements[Child]; 60 61 H->Elements[Parent] = Tmp; 62 return MinItem; 63 64 65 //建立最小堆 66 //元素已经读入 67 void PercDown(MinHeap H, int i) 68 69 int Parent, Child; 70 HuffmanTree Tmp = H->Elements[i]; 71 for (Parent = i; Parent * 2 <= H->Size; Parent = Child) 72 73 Child = Parent * 2; 74 if ((Child != H->Size) && (H->Elements[Child]->Weight > H->Elements[Child + 1]->Weight)) 75 Child++; 76 if (Tmp->Weight <= H->Elements[Child]->Weight)break; 77 else 78 H->Elements[Parent] = H->Elements[Child]; 79 80 H->Elements[Parent] = Tmp; 81 82 void BuildMinHeap(MinHeap H) 83 84 for (int i = H->Size / 2; i > 0; i--) //从最后一个有子节点的父节点开始 85 PercDown(H, i); 86 87 88 //利用最小堆 构建哈夫曼树 89 //每次把权值最小的两颗二叉树合并 90 HuffmanTree Huffman(MinHeap H) 91 //假设H->Size个权值已经存在H->Elements[]->Weight里 92 int i; 93 HuffmanTree T; 94 BuildMinHeap(H); //将H->Elements[]按照权重调整为最小堆 95 for (int i = 1; i < H->Size; i++) //H->Size个元素合并 总共需要H->Size-1次 96 97 T = (HuffmanTree)malloc(sizeof(struct TreeNode)); 98 T->Left = DeleteMin(H); //取出权重最小的元素 作为T的左节点 99 T->Right = DeleteMin(H); //取出权重最小的元素 作为T的右节点 100 T->Weight = T->Left->Weight + T->Right->Weight; //计算新的权重 101 Insert(H, T); //将新T插入最小堆中; 102 103 T = DeleteMin(H); 104 return T; 105
哈夫曼树的特点:
①没有度为1的节点
②n个叶子节点的哈夫曼树公有2n-1个节点
因为$n_0=n_2+1$
③哈夫曼树的任意非叶节点的左右子树交换后任是哈夫曼树
④对同一组权值 可能会存在不同构的哈夫曼树 但他们的WPL是一样的
PTA第12题 读入一串数 将其处理为 最小堆 并输出 从某个下标i到根节点的数据(因为最大堆是一个完全二叉树)
要注意数据个数 以及数据最小值 我最开始取 哨兵H[0]=-1 那么肯定负数 就无法正确处理
还有发现的问题是 用的方法不一样 生成的最小(大)堆结果不一样
建立最大(小)堆的方式有两种(就我所知):
① 通过插入操作 不断将元素插入 复杂度为 $O(NLogN)$
② 先将数据按顺序存入(满足完全二叉树的特征) 在调整各个节点的位置 复杂度为 $O(LogN)$
利用插入操作:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<malloc.h> 4 5 typedef struct HeapStruct* MinHeap; 6 struct HeapStruct 7 8 int* Elements; 9 int Size; 10 int Capacity; 11 ; 12 int IsFull(MinHeap H) 13 14 return (H->Capacity == H->Size) ? 1:0; 15 16 MinHeap Create(int MaxSize) 17 18 MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct)); 19 H->Elements = (int*)malloc(sizeof(int) * (MaxSize + 1)); 20 H->Capacity = MaxSize; 21 H->Size = 0; 22 H->Elements[0] = -10001; 23 return H; 24 25 void Print(MinHeap H, int num) 26 27 for (int i = num; i >= 1; i /= 2) 28 29 if (i > 1) 30 printf("%d ", H->Elements[i]); 31 else 32 printf("%d", H->Elements[i]); 33 34 printf("\n"); 35 36 void Insert(MinHeap H, int Element) 37 38 if (IsFull(H)) 39 return; 40 int i = ++H->Size; 41 for (; Element < H->Elements[i / 2]; i /= 2) 42 H->Elements[i] = H->Elements[i / 2]; 43 H->Elements[i] = Element; 44 45 46 int main() 47 48 MinHeap H; 49 H = Create(1001); 50 int N, M; 51 scanf("%d %d\n", &N, &M); 52 for (int i = 1; i < N + 1; i++) 53 54 int num; 55 scanf("%d", &num); 56 Insert(H, num); 57 58 for (int i = 0; i < M; i++) 59 60 int num; 61 scanf("%d", &num); 62 Print(H, num); 63 64 return 0; 65
利用读入 再 转换的操作
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<malloc.h> 4 5 typedef struct HeapStruct* MinHeap; 6 struct HeapStruct 7 8 int* Elements; 9 int Size; 10 int Capacity; 11 ; 12 13 MinHeap Create(int MaxSize) 14 15 MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct)); 16 H->Elements =(int*)malloc(sizeof(int) * (MaxSize + 1)); 17 H->Capacity = MaxSize; 18 H->Size = 0; 19 H->Elements[0]= -10001; 20 return H; 21 22 //建立最小堆 23 void PrecDown(MinHeap H, int i) //下滤 24 25 int Parent, Child; 26 int Tmp = H->Elements[i]; 27 for (Parent = i; Parent * 2 <= H->Size; Parent = Child) 28 29 Child = Parent * 2; 30 if ((Child != H->Size) && (H->Elements[Child]>H->Elements[Child + 1])) 31 Child++; 32 if (Tmp <= H->Elements[Child])break; 33 else 34 H->Elements[Parent] = H->Elements[Child]; 35 36 37 H->Elements[Parent] = Tmp; 38 39 void BuildMinHeap(MinHeap H) 40 41 int i; 42 for (i = H->Size / 2; i > 0; i--) 43 PrecDown(H, i); 44 45 void Print(MinHeap H, int num) 46 47 for (int i = num; i >= 1; i /= 2) 48 49 if(i>1) 50 printf("%d ", H->Elements[i]); 51 else 52 printf("%d", H->Elements[i]); 53 54 printf("\n"); 55 56 int main() 57 58 MinHeap H; 59 H = Create(1001); 60 int N, M; 61 scanf("%d %d\n", &N, &M); 62 for (int i = 1; i < N+1; i++) 63 64 int num; 65 scanf("%d", &num); 66 H->Elements[++H->Size] = num; 67 68 BuildMinHeap(H); 69 for (int i = 0; i < M; i++) 70 71 int num; 72 scanf("%d", &num); 73 Print(H, num); 74 75 return 0; 76
PTA第13题
并查集算法的实践 听了一点课后就做 答案部分正确 我的思路比较简单
课上讲利用数组来 简化表示集合 因为$N$个元素可以一一映射到$0\simN-1$
每次读入俩个数据 若是连接 就把第一个元素的父节点设置为第二个元素
若是判断是否连接 就插叙两个元素的父节点是否相等
这样做出来答案是错误的 没有Union操作 没有压缩路径操作
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 4 typedef int ElementType; 5 typedef ElementType SetName; 6 typedef ElementType SetType[10001]; 7 8 SetName Find(SetType S, ElementType X) 9 10 for (; S[X] >= 0; X = S[X]); 11 return X; 12 13 14 void Union(SetType S, ElementType X1, ElementType X2) 15 16 SetName Root1 = Find(S, X1); 17 SetName Root2 = Find(S, X2); 18 if (Root1 != Root2) S[Root2] = Root1; 19 20 21 int main() 22 23 SetType S; 24 for (int i = 0; i < 20; i++) S[i] = -1; 25 int N; 26 scanf("%d\n", &N); 27 char Cand; 28 while (scanf("%c",&Cand)&&Cand!=‘S‘) 29 30 int Com1, Com2; 31 scanf("%d%d\n", &Com1, &Com2); 32 if (Cand == ‘C‘) 33 34 if (Find(S, Com1) == Find(S, Com2)) 35 printf("yes\n"); 36 else 37 printf("no\n"); 38 39 else if (Cand == ‘I‘) 40 41 S[Com1] = Com2; 42 43 44 int Size = 0; 45 for (int i = 0; i < N; i++) 46 47 if (S[i] == -1) 48 Size++; 49 50 if (Size == 1) 51 printf("The network is connected."); 52 else 53 printf("There are %d components.", Size); 54 return 0; 55
以上是关于数据结构学习第十五天的主要内容,如果未能解决你的问题,请参考以下文章