• [Algo][July][Notes+Homework]并查集的应用


    并查集顾名思义就是有“合并集合”和“查找集合”两种操作的关于数据结构的一种算法。

    假设n个节点,初始时点与点之间没有连接
    • 给出一系列的连接操作
    • 一次连接操作不产生环,则接受,否则被抛弃 

    算法课上老师出的题目太抽象了。先引入一个简单具体的问题:

    reference website:

    //概念描述:

    https://segmentfault.com/a/1190000004023326

    //例题描述

    https://segmentfault.com/a/1190000000752006

    假如已知有n个人和m对好友关系(存于数字r)。如果两个人是直接或间接的好友(好友的好友的好友...),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋友圈。
    假如:n = 5 , m = 3 , r = {{1 , 2} , {2 , 3} , {4 , 5}},表示有5个人,1和2是好友,2和3是好友,4和5是好友,则1、2、3属于一个朋友圈,4、5属于另一个朋友圈,结果为2个朋友圈。

    从这道题的角度出发,也就是有5个节点,3条边(1条边表示两个节点之间有关系)。要把互相能到达的节点合并成一个集合。最后判断父节点的个数,就是有多少个朋友圈。

    1. 如何表示节点:

    机构体表示法:

    #define MAX 10000
    struct Node
    {
        int data;    //数据
        int rank;    //层级
        int father; //父节点
     }node[MAX];

    数组表示法:

    int father[max]; //集合index的类别
    int rank[max];//集合index的层次,通常初始化为0,C++里面用rank会和某个关键字冲突,所以C++里面要换一个数组名
    int data[max];//集合index的数据类型

    2.初始化节点:

    void init(int i){
        father[i] = i; //初始化,每个人的父节点都是自己
        rank[i] = 0; //每层都是0
    }

    3.合并节点:

     1 void unionSet(int a, int b){
     2     a = find(a); //找a的父节点,这时候的变量a已经是自己的父节点了
     3     b = find(b); //找b的父节点,这时候的变量b也是自己的父节点,下面代码注意区分
     4     if(a == b){   //a和b的父节点相同,属于同一集合,不需要合并
     5         return;
     6     }
     7     if(rank[a]>rank[b]){ //a的层数比b的层数大
     8         father[b] = a;  //把父节点b指向a,b的父节点变成了a
     9     }
    10     else {
    11         father[a] = b;  //a的的父节点变成b
    12         if(rank[a]==rank[b]){
    13             rank[b]++;
    14         }
    15     } 
    16 }

    4.寻找父节点:

    1 int find(int x){ //寻找父节点
    2     if(x == father[x]){  //如果这个节点是父节点则返回
    3         return x;
    4     }
    5     else{
    6         return find(father[x]);  //如果这个节点不是父节点,那么再往上寻找这个节点的father的father
    7     }
    8 }

    在这个过程中可以进行路径压缩:

    1 int findSet(int d){
    2     if(d != father[d]){
    3         father[d] = findSet(father[d]); //使这条路径上的所有子节点直接指向父节点
    4     }
    5     return father[d];
    6 }

     5. 问题答案:

     1 #include<cstdio>  
     2 #include<iostream>
     3 using namespace std;
     4 #define MAX 6
     5 int father[MAX];
     6 int ran[MAX];
     7 void init(){
     8     for(int i = 1;i < MAX;i++){
     9         father[i]=i;
    10         ran[i] = 0;
    11     }
    12 }
    13 
    14 int findSet(int d){
    15     if(d != father[d]){
    16         father[d] = findSet(father[d]);
    17     }
    18     return father[d];
    19 }
    20 
    21 void unionSet(int a, int b){
    22     a = findSet(a); 
    23     b = findSet(b); 
    24 
    25     if(a == b){   //我自己在写的时候,这其中犯了一个致命的错误,if里面的恒等条件写成了赋值,导致合并集合失败。
    26         return;
    27     }
    28     if(ran[a]>ran[b]){ 
    29         father[b] = a;  
    30     }
    31     else {
    32         father[a] = b;  //a的的父节点变成b
    33         if(ran[a]==ran[b]){
    34             ran[b]++;
    35         }
    36     }
    37 }
    38 
    39 int main()  
    40 {  
    41     init();
    42     int m[3][2]={{1,2},{2,3},{4,5}};
    43     int col = sizeof(m[0])/sizeof(int); //计算列数
    44     int row = (sizeof(m)/sizeof(int))/col; //计算行数
    45     int i,j,sum = 0;
    46     for(i = 0; i< row; i++){
    47         j=0;
    48         while(j<col){
    49             unionSet(m[i][j],m[i][j++]); //输入原始关系
    50             j++;
    51         }       
    52     }
    53     for(i = 1; i<MAX; i++){
    54         cout<<"i:"<<i<<" father:"<<father[i];
    55         cout<<" rank:"<<ran[i]<<'
    ';
    56         if(findSet(i)==i) sum++; //找有几个根节点,就是有几个朋友圈
    57     }
    58     cout<<"sum: "<<sum<<endl;      
    59 }  

    输出:

    1 Finished in 0 ms
    2 i:1 father:1 rank:1
    3 i:2 father:1 rank:0
    4 i:3 father:1 rank:0
    5 i:4 father:4 rank:1
    6 i:5 father:4 rank:0
    7 sum: 2

     6. 课堂作业

    思路:老师也没说要做什么操作,然后又说不成环,我猜想就是如果成环的话,就没有父节点了。

     1 int main()  
     2 {  
     3     init();
     4     int m[5]={1,2,3,4,5};
     5     //cout<<col;
     6     int i,j,sum = 0;
     7     for(i = 0; i<5-1; i++){
     8         unionSet(m[i],m[i+1]);
     9     }       
    10     for(i = 1; i<MAX; i++){
    11         cout<<"i:"<<i<<" father:"<<father[i];
    12         cout<<" rank:"<<ran[i]<<'
    ';
    13         if(findSet(i)==i) sum++; //做了路径压缩
    14     }
    15     cout<<"sum: "<<sum<<endl;      
    16 }  

    输出:

    1 i:1 father:2 rank:0
    2 i:2 father:2 rank:1
    3 i:3 father:2 rank:0
    4 i:4 father:2 rank:0
    5 i:5 father:2 rank:0
    6 sum: 1

    sum应该是要>0吧。。。先这样记录,等学习到后面再来更新~

  • 相关阅读:
    让所有IE支持HTML5的解决方案
    分享按钮源码
    jQuery引用
    字体 ttf 下载
    重写 gallery 防止滚过头
    view 隐藏 显示
    android 数据存储几个方式
    TextView 内容居中
    eclipse导入基类方法
    imageview 全屏 拖动
  • 原文地址:https://www.cnblogs.com/hopping/p/7699785.html
Copyright © 2020-2023  润新知