zoukankan      html  css  js  c++  java
  • 数据结构学习第十五天

    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("
    ");
    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, &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("
    ");
    55 }
    56 int main()
    57 {
    58     MinHeap H;
    59     H = Create(1001);
    60     int N, M;
    61     scanf("%d %d
    ", &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);
    27     char Cand;
    28     while (scanf("%c",&Cand)&&Cand!='S')
    29     {
    30         int Com1, Com2;
    31         scanf("%d%d
    ", &Com1, &Com2);
    32         if (Cand == 'C')
    33         {
    34             if (Find(S, Com1) == Find(S, Com2))
    35                 printf("yes
    ");
    36             else
    37                 printf("no
    ");
    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);
     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
    ", &Com1, &Com2);
     60         if (Cand == 'C')
     61         {
     62             if (Find(S, Com1) == Find(S, Com2))
     63                 printf("yes
    ");
     64             else
     65                 printf("no
    ");
     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
    ", &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
    ", &u, &v);
     93     if (Find(S, u - 1) == Find(S, v - 1))
     94         printf("yes
    ");
     95     else printf("no
    ");
     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.
    ");
    107     else
    108         printf("There are %d components.
    ", 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

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

  • 相关阅读:
    对象结构型
    对象结构型
    对象行为型模式
    定时任务(二)
    定时任务(一)
    kill端口-更新sql-添加字段
    获取ip和端口号
    List集合中的末位元素置首位
    首页报表数据展示(一)
    具体的类中包括枚举类写法
  • 原文地址:https://www.cnblogs.com/57one/p/11434189.html
Copyright © 2011-2022 走看看