数据结构学习第十五天

11:36:53 2019-08-30

学习

 09:42:25 2019-08-31

补完昨天未写完的并查集优化

 

带权路径长度(WPL) (Weighted Path Length of Tree),设二叉树有n个叶子节点,每个叶子节点带有权值${w_k}$,从根节点到每个叶子节点的长度为${l_k}$,则每个叶子节点的带权路径长度之和就是:${WPL}=\displaystyle \sum_{k=1}^n{w_k}{l_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 }
View Code

哈夫曼树的特点:

①没有度为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 }
View Code

利用读入 再 转换的操作

 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 }
View Code

 

并查集 简单版本

 1 #include<stdio.h>
 2 
 3 //利用树结构表示集合,树的每个节点代表一个集合元素
 4 //利用双亲表示法:孩子指向双亲
 5 
 6 //利用数组实现集合及运算
 7 typedef struct
 8 {
 9     int Data;
10     int Parent;
11 }SetType;
12 
13 //查找某个元素所在的集合 (用根节点表示) 返回节点下标
14 int Find(SetType S[], int Element)
15 {
16     int i;
17     for (i = 0; i<MaxSize&&S[i].Data != Element; i++);   //MaxSize是集合最大最大个数
18     if (i >= MaxSize)return -1;   //未找到Element ,返回-1
19     for (; S[i].Parent >= 0;i = S[i].Parent);
20     return i;
21 }
22 
23 void Union(SetType S[], int X1, int X2)
24 {
25     int Root1;
26     int Root2;
27     Root1 = Find(S, X1);
28     Root2 = Find(S, X2);
29     if (Root1 != Root2) S[Root2].Parent = Root1;
30 }
31 
32 
33 //集合的简化表示
34 //任何有限集合的(N个)元素都可以被一一映射为整数 0 到 N-1
35 //利用数组下标存 元素  数组中的元素 为父节点
36 
37 typedef int ElementType; //默认元素用非负整数表示
38 typedef int SetName;     //默认用根节点下标作为集合名称
39 typedef ElementType SetType[MaxSize];
40 
41 SetName Find(SetType S, ElementType X)
42 {/*默认集合元素全部初始化为-1*/
43     for (; S[X] >= 0; X = S[X]);
44     return X;
45 }
46 void Union(SetType S, SetName Root1, SetName Root2)
47 {/*默认Root1和Root2是不同集合的根节点*/
48     S[Root2] = Root1;
49 }
View Code

PTA第13题  

并查集算法的实践  听了一点课后就做 答案部分正确 我的思路比较简单 

课上讲利用数组来 简化表示集合 因为$N$个元素可以一一映射到${0}\sim{N-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 }
View Code

 学到了陈越姥姥的新词 TSSN  too simple sometimes naive

将 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     if (S[X]< 0)
 15         return X;
 16     else
 17         return S[X] = Find(S, S[X]);
 18 }
 19 
 20 void Union(SetType S, ElementType X1, ElementType X2)  //不管是按高度还是按规模 两者统称按秩归并
 21 {                                                        //最坏情况下的树高是O(LogN)
 22     SetName Root1 = Find(S, X1);
 23     SetName Root2 = Find(S, X2);
 24     //if (Root1 != Root2) S[Root2] = Root1;  //最开始TSSN的做法
 25 
 26     /*if (Root1 == Root2)return;   
 27     if (S[Root1] < S[Root2])    //利用规模来计算 
 28     {
 29         S[Root1] += S[Root2];
 30         S[Root2] = Root1;
 31     }
 32     else
 33     {
 34         S[Root2] += S[Root1];
 35         S[Root1] = Root2;
 36     }*/
 37 
 38     if (S[Root1] < S[Root2])  //这里用的是树高
 39         S[Root2] = Root1;
 40     else if(S[Root2]<S[Root1])
 41         S[Root1] = Root2;
 42     else                    //树高相等
 43     {
 44         S[Root1]--;
 45         S[Root2] = Root1;
 46     }
 47 }
 48 
 49 /*int main()   //我的写法
 50 {
 51     SetType S;
 52     int N;
 53     scanf("%d\n", &N);
 54     for (int i = 0; i <N+1; i++) S[i] = -1;
 55     char Cand;
 56     while (scanf("%c",&Cand)&&Cand!='S')
 57     {
 58         int Com1, Com2;
 59         scanf("%d%d\n", &Com1, &Com2);
 60         if (Cand == 'C')
 61         {
 62             if (Find(S, Com1) == Find(S, Com2))
 63                 printf("yes\n");
 64             else
 65                 printf("no\n");
 66         }
 67         else if (Cand == 'I')
 68             Union(S, Com1, Com2);
 69     }
 70     int Size = 0;
 71     for (int i =1; i < N+1; i++)
 72     {
 73         if (S[i]<0)
 74             Size++;
 75     }
 76     if (Size == 1)
 77         printf("The network is connected.");
 78     else
 79         printf("There are %d components.", Size);
 80     return 0;
 81 }*/
 82 
 83 void Input_connection(SetType S)
 84 {
 85     ElementType u, v;
 86     scanf("%d %d\n", &u, &v);
 87     Union(S,u - 1, v - 1);
 88 }
 89 void Check_connection(SetType S)
 90 {
 91     ElementType u, v;
 92     scanf("%d %d\n", &u, &v);
 93     if (Find(S, u - 1) == Find(S, v - 1))
 94         printf("yes\n");
 95     else printf("no\n");
 96 }
 97 void Check_network(SetType S,int n)
 98 {
 99     int Size = 0;
100     for (int i = 0; i < n; i++)
101     {
102         if (S[i]<0)
103             Size++;
104     }
105     if (Size == 1)
106         printf("The network is connected.\n");
107     else
108         printf("There are %d components.\n", Size);
109 }
110 void Initialization(SetType S, int n)
111 {
112     for (int i = 0; i < n; i++)
113         S[i] = -1;
114 }
115 int main()
116 {
117     SetType S;
118     int n;
119     char in;
120     scanf("%d", &n);
121     Initialization(S, n);
122     do
123     {
124         scanf("%c", &in);
125         switch (in)
126         {
127         case 'I':Input_connection(S); break;
128         case 'C':Check_connection(S); break;
129         case 'S':Check_network(S,n); break;
130         }
131     }while (in != 'S');
132     return 0;
133 }
View Code

 还有学习到的是 尾递归在程序运行过程中 会被计算机转化为循环  所以不会把内存爆掉

posted @ 2019-08-30 23:16  57one  阅读(186)  评论(0)    收藏  举报