並查集-----小米麪試題之朋友圈問題

先來看一下題目:

已知有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個朋友圈

爲了追求高效且準確,我們用到了並查集來完成這道題

一、並查集?

並查集就是將n個不同的元素劃分成爲一組不相交的集合。開始時,每個元素自成一個單元素集合,然後按照一定的規律將歸於同一組元素的集合合併。

它是一種樹形的數據結構,每個集合以一棵樹表示,樹的每一個結點代表集合的一個單元素。所有各個集合的全集合構成一個森林,並用樹與森林的父指針表示來實現。其下標代表元素名。第i個數組元素代表集合元素i的樹結點。樹的根結點的下標表示集合名稱,根結點的父爲-1,表示集合中元素個數

二、圖示表示一下
以上面的題的爲基礎爲例:
n=5,m=3,r={{1,2},{2,3},{4,5}},表示有5個人,1和2是好友,2和3是好友,4和5是好友,則1、2、3屬於一個朋友圈,4、5屬於一個朋友圈。最終結果就是2個朋友圈

集合爲:r={1,2,3,4,5}
開始時,每個元素就是一個集合
這裏寫圖片描述

按照上面所給的要求將集合中的元素合併成爲三個子集合
r1 = {1,2}
r2 = {2,3}
r3 = {4,5}

用樹狀來表示所形成的朋友圈就是:
這裏寫圖片描述

與此同時,我們將好友關係用數組的方式來記錄,因爲2是1的朋友,所有就將2的下標疊加到1的下面去,2下標下的數據也存放在的1的下面。3是2的朋友,2是1的朋友,所以就把3的下標疊加到1的下面去,3下標下的數據也存放在的1的下面。4和5的朋友圈如同上述
這裏寫圖片描述
這裏寫圖片描述
在同一棵樹上的所有節點所表示的集合元素在同一個子集合中。對於任意給定的集合元素x,只要藉助這個映射就能找到存放x的樹結點,沿此存放x結點的父指針向上一直走到樹的根結點,就可以得到x所在集合的名字。

總結:

* (1)只要某一個下標裏的數據爲負數,則它就是其所在朋友圈的根。負數的大小表示着朋友圈的人數(eg:下標1中的數據爲-3,則其所在的朋友圈人數爲3)
* (2)數組中有幾個負數就表示有幾個朋友圈
* (3)可以根據下標中所對應的數字找到根節點,相同根節點的就在同一個朋友圈內

下面來看看代碼實現:

#include <iostream>
using namespace std;
#include <vector>
class UnionFriend
{
public:
      // 構造函數(用-1初始化)
      UnionFriend(int size)
            :_set(size, -1)
      {
            // 其他方式
            // _set.resize(size, -1)
            // _set.assign(size, -1)
      }
      // 求交集
      void Union(int x1, int x2)
      {
            // 先找x1、x2的根
            int root1 = FindRoot(x1);
            int root2 = FindRoot(x2);
            //比較x1和x2的根是否相等
            if (root1 != root2)
            {
                  _set[root1] += _set[root2];
                  _set[root2] = root1;
            }
      }
      // 求合併後集合的個數
      size_t count()
      {
            size_t count = 0;
            //統計負數的個數
            for (size_t i = 0; i < _set.size(); i++)
            {
                  if (_set[i] < 0)
                  {
                        count++;
                  }
            }
            return count;
      }
private:
      //查找根節點
      int FindRoot(int index)
      {
            while (_set[index] >= 0)
            {
                  index = _set[index];
            }
            return index;
      }
private:
      //一段連續的空間
      std::vector<int> _set;
};
int friends(const int m, const int n, int r[][2])
{
      //讓下標從1開始,符合題目
      UnionFriend u(m + 1);
      for (int i = 0; i < n; i++)
      {
            u.Union(r[i][0], r[i][1]);
      }
      return u.count() - 1;
}
void Test()
{
      int r[][2] = { {1,2},{2,3},{4,5} };
      cout << "朋友圈個數:" << friends(5, 3, r) << endl;
}
int main()
{
      Test();
      system("pause");
      return 0;
}
發佈了55 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章