并查集(Union Find Set)
并查集(Union Find Set)
這篇文章我們來講一下并查集,什么是并查集呢?來舉一個形象的例子。
話說符文之地上居住著各式各樣的英雄,他們效忠于自己的國家或部落,整天背著武器在外面執行任務,碰到和自己不是一隊的,就免不了要打一架。英雄們都有一個優點就是講義氣,絕對不打自己的隊友,而且他們信奉“隊友的隊友就是我的隊友”,只要是能通過隊友關系串聯起來的,不管拐了多少個彎,都認為是自己人。這樣一來,不在同一個陣營的人,無論如何都無法通過隊友關系連起來,就可以放心往死里打了。
兩個原本互不相識的人,怎么判斷是否屬于一個陣營呢?我們可以在每個陣營內推舉出一個比較有名望的人,作為該陣營的代表人物。比如德瑪西亞的君主雖然是選舉出來的,但每一代的嘉文都能成功登上王位。現任君主是嘉文三世,他的兒子基本可以確定是下一代君主。這樣,每個陣營都有了自己的領軍人物,或者叫隊長,兩人開打之前,只要互相確認一下自己的隊長是不是同一個人,就可以確定敵友關系了。
還有一個問題啊,英雄們只知道自己直接的隊友是誰,很多人壓根就不認識隊長,要判斷自己的隊長是誰,只能漫無目的的通過隊友的隊友關系問下去:“你是不是隊長?你是不是隊長?”這樣一來,隊長面子上掛不住了,而且效率太低,還有可能陷入無限循環中。于是隊長下令,重新組隊,隊內所有人實行分等級制度,形成樹狀結構,隊長就是根節點,下面分別是二級隊員、三級隊員,每個人只要記住自己的上級是誰就行了。遇到判斷敵友的時候,只要一層層向上問,直到最高層,就可以在短時間內確定隊長是誰了。
int setSize = 0; unordered_map<int, int> father; int find(int x) {int root = x;while (this->father[root] != -1) {root = this->father[root];}return root; }有一天,遠在地球的孫悟空,發現了能量波動的符文大陸,于是分身來到了這里,這對于符文大陸來說,孫悟空屬于新來的英雄,不屬于任何陣營,那么它的上級就是它自己。我們定義一個add函數,參數x表示新英雄的ID,首先得確保這個ID不在原來的任何一個陣營里,然后把它的上級設置成-1表示它就是隊長,最后讓陣營的數量+1。
void add(int x) {if (!this->father.count(x)) {this->father[x] = -1;this->setSize++;} }孫悟空來到符文大陸之后,由于旅途的奔波,讓大圣不負原來世界的威能,經過猴子長者的指導,去北方尋找猴群,偶遇易大師,拜于無極劍道。易大師后來帶猴子去阿貍的包子鋪吃包子,阿貍見猴子無家可歸,很可憐,就收留了猴子,這兩個動物日久生情,成為了跨越種族的情侶。
九尾妖狐阿貍是屬于艾歐尼亞陣營的,猴子也就順理成章的加入了艾歐尼亞(找了個有戶口的女人),怎么加入呢?
我們把猴子的ID-x和阿貍的ID-y都傳進merge函數中,然后通過find函數分別找到x和y的根節點,如果rootX != rootY,說明兩個節點不是屬于一個陣營的,我們把father[rootX] = rootY,這樣就相當于把x加入到了y的陣營,最后讓陣營的數量-1。
有一天,猴子出去執行任務,正好遇到了巖雀塔莉埡,但他倆互相不認識,不知道能不能動手,就開始找自己的上頭問,猴子就去問自己的師傅無極劍圣——易大師,認不認識一個在石頭上滑滑板的小孩,巖雀也去問自己的師傅疾風劍豪——亞索,認不認識一只拿著棒子的猴子。劍圣表示不認識啊,我去問問艾歐尼亞的領袖卡爾瑪,亞索也表示,不知道什么猴子,也得去問問卡爾瑪。一路問上去,最后才確定,兩個人都是艾歐尼亞的。
這樣一連串的問太麻煩了,怎么辦呢,干脆把所有的隊員都直接掛在隊長的旗下,出了事直接問隊長,這樣效率也高,所以我們得把整個陣營的層數維持在較低的水平上,也就是路徑壓縮算法。
int find(int x) {int root = x;while (this->father[root] != -1) {root = this->father[root];}while (x != root) {int original = this->father[x];this->father[x] = root;x = original;}return root; }算法模板
class UnionFindSet{private:int setSize = 0;unordered_map<int, int> father;public:int find(int x) {int root = x;while (this->father[root] != -1) {root = this->father[root];}while (x != root) {int original = this->father[x];this->father[x] = root;x = original;}return root;}bool isUnion(int x, int y) {return this->find(x) == this->find(y);}void merge(int x, int y) {int rootX = this->find(x);int rootY = this->find(y);if (rootX != rootY) {father[rootX] = rootY;this->setSize--;}}void add(int x) {if (!this->father.count(x)) {this->father[x] = -1;this->setSize++;}} };應用
總結
以上是生活随笔為你收集整理的并查集(Union Find Set)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拓扑排序(Topological Sor
- 下一篇: 2014/Province_Java_B