【數據結構——并查集】

引入

并查集(Disjoint Set Union,DSU)是一種用于管理元素分組的數據結構。

合并(Union):將兩個不相交的集合合并為一個集合。
查找(Find):確定某個元素屬于哪個集合,通常通過返回集合的“代表元素”(groupID或父節點)實現。

quickFind 和 quickUnion 是并查集的兩種實現方式。

每個元素初始時是一個獨立的集合,其groupID是本身下標或父節點指向自己(分別表明各自屬于哪個集合)。
如下:
在這里插入圖片描述
主要就是對兩個數組所存的內容進行操作,特別是代表元素部分。
對代表元素進行操作的方向(思考角度)不同,就會使用不同的解決方案(如選擇quickFind還是quickUnion,)

quickFind

每個元素直接指向其所屬集合的代表元(根節點),合并操作時需要遍歷整個數組更新所有相關元素。

時間復雜度:
查找(Find):O(1),直接訪問數組即可確定所屬集合。
合并(Union):O(n),需要遍歷數組更新所有屬于同一集合的元素。

特點:查找速度快,但合并效率低(找快合慢)。

在這里插入圖片描述

頭文件

#pragma once
typedef int Element;typedef struct {Element* data;	 //存儲具體數據組編號int* groupID;    //存儲組編號int n;           //元素個數
}QuickFindSet;QuickFindSet* createQuickFindSet(int n);		//創建n個元素的quickFind樹(快查樹)
void releaseQuickFindSet(QuickFindSet* setQF);	//釋放setQF這個樹
void initQuickFindSet(const QuickFindSet* setQF, const Element* data, int n);//初始化setQF這個含n個結點的快查樹,以及data數組的值(groupID數組初始時常以下標為值,依次賦值就行了)
//查,判斷兩個元素是不是同一個組
int isSameQF(QuickFindSet* setQF, Element a, Element b);
//并,合并兩個元素
void unionQF(QuickFindSet* setQF, Element a, Element b);

功能實現

創建

QuickFindSet* createQuickFindSet(int n) {QuickFindSet* setQF = (QuickFindSet*)malloc(sizeof(QuickFindSet));if (setQF == NULL) {printf("setQF malloc failed!\n");return NULL;}//若申請成功,就傳遞n的值并繼續申請兩個數組的空間setQF->n = n;setQF->data =(Element*) malloc(sizeof(Element) * n);setQF->groupID = (int*)malloc(sizeof(int) * n);return setQF;
}

釋放

void releaseQuickFindSet(QuickFindSet* setQF) {if (setQF) {if (setQF->data) {free(setQF->data);}if (setQF->groupID) {free(setQF->groupID);}free(setQF);}
}

初始化

void initQuickFindSet(const QuickFindSet* setQF, const Element* data, int n) {for (int i = 0; i < n; ++i) {setQF->data[i] = data[i];setQF->groupID[i] = i;}
}

查找目標值的索引

static int findIndex(const QuickFindSet* setQF, Element e) {for (int i = 0; i < setQF->n; ++i) {if (setQF->data[i] == e) {return i;}}return -1;
}

判斷目標值索引對應的組ID是否相同(a、b在不在一個集合)

int isSameQF(QuickFindSet* setQF, Element a, Element b) {int aIndex = findIndex(setQF, a);int bIndex = findIndex(setQF, b);if (aIndex == -1 || bIndex == -1) {return 0;}return setQF->groupID[aIndex] == setQF->groupID[bIndex];
}

合并操作

void unionQF(QuickFindSet* setQF, Element a, Element b) {int aIndex = findIndex(setQF, a);int bIndex = findIndex(setQF, b);//假設把b集合中的元素合并到a集合上int gID = setQF->groupID[bIndex];		//先存下b的組號for (int i = 0; i < setQF->n; ++i) {if (setQF->groupID[i] == gID) {                 //是b組的元素setQF->groupID[i] = setQF->groupID[aIndex]; //全部組號改為a組的組號}}
}

功能調用

void test250808() {int n = 9;QuickFindSet* QFSet = createQuickFindSet(n);Element data[9];for (int i = 0; sizeof(data) / sizeof(data[0]); ++i) {data[i] = i;}initQuickFindSet(QFSet, data, n);unionQF(QFSet, 1, 3);unionQF(QFSet,3, 2);unionQF(QFSet, 2, 4);if (isSameQF(QFSet, 0, 2)) {printf("Yes\n");}else {printf("No\n");}unionQF(QFSet, 5, 1);if (isSameQF(QFSet, 5, 2)) {printf("Yes\n");}else {printf("No\n");}releaseQuickFindSet(QFSet);
}int main() {test250808();
}

quickUnion

使用樹結構表示集合(看下圖只能體現鏈式存儲,后面的內容會講到路徑壓縮:通過增大節點的度來提高效率會體現出樹的特點),每個節點的parent指向其父節點,根節點
的parent指向自身,合并時將一個樹的根指向另一個樹的根就能連接兩個樹。

時間復雜度:
查找(Find):O(logn)(平均,取決于樹高),需要遞歸或迭代找到根節點。
合并(Union):O(logn),僅需修改根節點的指向。

特點:合并效率高,但查找速度取決于樹高。

在這里插入圖片描述
這里是子節點指向父節點;父節點是自己時就指向自己,這種節點被稱為根節點(如1和5)。
在這里插入圖片描述
quickUnion查找速度取決于樹高,可通過路徑壓縮等進一步提升性能:

路徑壓縮

路徑壓縮就是想辦法把樹變矮。像上面的例子:從1到4這串右子樹,如果從根節點開始往下遍歷,將每個節點的parent都直接指向根節點1,當根節點查找目標節點時僅需訪問1層。

如何實現這一思想呢?遍歷這些節點以及將它們每個節點的parent指向根節點這一環節時,可以想象成從葉子節點開始挨個摘取并存下來(直接指向根節點的葉子節點忽略),遍歷到根節點時再挨個取出來并挨個將節點的parent指向根節點。

這種存儲特點就不由的聯想起了棧,相對提前申請空間的順序棧,鏈式棧更合適,用多少就申請多少。且需采用頭插法進行入棧操作(方便便利插入節點之后的節點元素)。
在這里插入圖片描述

與quickFind的代碼很相似,個別地方需調整。

頭文件

#pragma once
typedef int Element;typedef struct {Element* data;int* parent;int* size;  //表示其中某個集合的元素個數int n;		//表示總元素個數(所有集合的元素個數之和)
}QuickUnionSet;//為壓縮存儲中鏈式棧的入棧出棧操作做準備:定義節點結構
typedef struct _set_node {int index;struct _set_node* next;
}SetNode;QuickUnionSet* createQuickUnionSet(int n);
void releaseQuickUnionSet(QuickUnionSet* setQU);
void initQuickUnionSet(QuickUnionSet* setQU, const Element* data, int n);int isSameQU(const QuickUnionSet* setQU, Element a, Element b);
void unionQU(QuickUnionSet* setQU, Element a, Element b);

功能實現

創建

QuickUnionSet* createQuickUnionSet(int n) {QuickUnionSet* setQU = (QuickUnionSet*)malloc(sizeof(Element) * n);if (setQU == NULL) {return NULL;}setQU->data = (Element*)malloc(sizeof(Element) * n);setQU->parent = (int*)malloc(sizeof(int) * n);setQU->size = (int*)malloc(sizeof(int) * n);setQU->n = n;return setQU;
}

釋放

void releaseQuickUnionSet(QuickUnionSet* setQU) {if (setQU) {if (setQU->data) {free(setQU->data);}if (setQU->parent) {free(setQU->parent);}if (setQU->size) {free(setQU->size);}}
}

初始化

void initQuickUnionSet(QuickUnionSet* setQU, const Element* data, int n) {for (int i = 0; i < n; i++) {setQU->data[i] = data[i];setQU->parent[i] = i;setQU->size[i] = 1;}
}

查找目標值索引

static int findIndex(const QuickUnionSet* setQU, Element e) {for(int i = 0; i < setQU->n; ++i) {if (setQU->data[i] == e) {return i;}}return -1;
}

路徑壓縮

#define PATH_COMPRESS
#ifndef PATH_COMPRESS
static int findRootIndex(const QuickUnionSet* setQU, Element e) {int curIndex = findIndex(setQU, e);if (curIndex == -1) {return -1;}
//若找到則向上遍歷,直到父節點和自身索引一致(找到根節點)while(setQU->parent[curIndex]!=curIndex) {curIndex = setQU->parent[curIndex];}return curIndex;
}
#else
static SetNode* push(SetNode* stack, int index) {
//傳入的stack是一個空指針,相當于無頭結點、頭指針鏈式結構的頭插操作SetNode* node = (SetNode*)malloc(sizeof(SetNode));node->index = index;node->next = stack;return node;
}static SetNode* pop(SetNode* stack, int* index) {SetNode* tmp = stack;
//主要任務是獲得棧頂元素的索引*index = stack->index;//之后更新棧頂索引stack = stack->next;free(tmp);return stack;
}static int findRootIndex(const QuickUnionSet* setQU, Element e) {int curIndex = findIndex(setQU, e);if (curIndex == -1)return -1;
//找根的路徑:從目標節點開始往上找,途經的節點都挨個入棧,到根節點為止SetNode* path = NULL;while (setQU->parent[curIndex] != curIndex) {path = push(path, curIndex);curIndex = setQU->parent[curIndex];}
//出棧,并將出棧的每個節點的parent都指向根節點while (path) {int pos;path = pop(path, &pos);setQU->parent[pos] = curIndex;}return curIndex;
}
#endif // !PATH_COMPRESS

判斷兩集合根的異同

int isSameQU(const QuickUnionSet* setQU, Element a, Element b) {int aRoot = findRootIndex(setQU, a);int bRoot = findRootIndex(setQU, b);if (aRoot == -1 || bRoot == -1) {return 0;}return aRoot == bRoot;
}

合并

/* 將元素a和元素b進行合并* 1. 找到a和b的根節點,原本是b的父節點指向a的父節點* 2. 引入根節點的size有效規則,誰的元素多,讓另外一個接入元素多的組*/void unionQU(QuickUnionSet* setQU, Element a, Element b) {int aRoot = findRootIndex(setQU, a);int bRoot = findRootIndex(setQU, b);if (aRoot == -1 || bRoot == -1) {return;}if (aRoot != bRoot) {    // a和b不在一個集合里
// 根據根節點的索引找到對應size空間里保存的根所在樹的總節點數//初始時size都為1,節點都是單獨占一個集合,結合時再將兩集合的size相加int aSize = setQU->size[aRoot];int bSize = setQU->size[bRoot];if (aSize >= bSize) {	// 將b元素的根節點指向a元素的根節點setQU->parent[bRoot] = aRoot;setQU->size[aRoot] += bSize;}else {setQU->parent[aRoot] = bRoot;setQU->size[bRoot] += aSize;}}
}

功能調用

void test250810() {int n = 9;QuickUnionSet* QUSet = createQuickUnionSet(n);Element data[9];for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {data[i] = i;}initQuickUnionSet(QUSet, data, n);unionQU(QUSet,1, 3);unionQU(QUSet, 3, 2);unionQU(QUSet, 2, 4);if (isSameQU(QUSet, 0, 2)) {printf("Yes\n");}else {printf("No\n");}unionQU(QUSet, 5, 1);if (isSameQU(QUSet, 5, 2)) {printf("Yes\n");}else {printf("No\n");}releaseQuickUnionSet(QUSet);
}
int main() {test250810();
}

拜拜~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95145.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95145.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95145.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在 Vue 中使用 ReconnectingWebSocket實現即時通訊聊天客服功能

在 Vue 中使用 ReconnectingWebSocketReconnectingWebSocket 是一個自動重連的 WebSocket 實現&#xff0c;非常適合在 Vue 項目中使用。下面是如何在 Vue 中集成和使用它的方法&#xff1a;搜索 "程序員老狼"安裝 ReconnectingWebSocket首先&#xff0c;你需要安裝…

智能體革命:網絡安全人的角色重塑與突圍指南

AI賦能千行百業的趨勢不可逆轉&#xff0c;當AI學會滲透測試&#xff0c;安全工程師的出路在哪里&#xff1f; 2025年8月7日&#xff0c;OpenAI正式發布GPT-5的消息刷屏科技圈。這個達到博士生水平的“統一”人工智能模型&#xff0c;將AI幻覺率降低60%&#xff0c;成本下降45%…

用于水T1值和脂肪分數量化的上半身自由呼吸磁共振指紋成像|文獻速遞-醫學影像算法文獻分享

Title題目Upper-body free-breathing Magnetic Resonance Fingerprinting applied tothe quantification of water T1 and fat fraction用于水T1值和脂肪分數量化的上半身自由呼吸磁共振指紋成像 01文獻速遞介紹磁共振指紋成像&#xff08;MRF&#xff09;是十年前推出的一種高…

Apache RocketMQ:消息可靠性、順序性與冪等處理的全面實踐

Apache RocketMQ 是一個高性能、高可靠的分布式消息中間件&#xff0c;廣泛應用于異步通信、事件驅動架構和分布式系統中。本文深入探討 RocketMQ 的消息可靠性、順序性和冪等處理機制&#xff0c;結合 Redisson 分布式鎖實現冪等消費&#xff0c;提供詳細的代碼示例和實踐建議…

無服務器日志分析由 Elasticsearch 提供支持,推出新的低價層

作者&#xff1a;來自 Elastic Log Analytics Elastic Observability Logs Essentials 在 Elastic Cloud Serverless 上提供成本效益高、無麻煩的日志分析。 SREs 可以攝取、搜索、豐富、分析、存儲和處理日志&#xff0c;而無需管理部署的運營開銷。[](https://www.elastic.co…

(Arxiv-2025)Phantom-Data:邁向通用的主體一致性視頻生成數據集

Phantom-Data&#xff1a;邁向通用的主體一致性視頻生成數據集 paper是字節發布在Arxiv2025的工作 paper title&#xff1a;Phantom-Data: Towards a General Subject-Consistent Video Generation Dataset Code&#xff1a;鏈接 Abstract 近年來&#xff0c;主體到視頻&#…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘mlflow’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘mlflow’問題 摘要 在Python開發中&#xff0c;pip install 報錯是一種常見問題&#xff0c;尤其是在使用集成開發環境&#xff08;IDE&#xff09;如PyCharm時…

2020/12 JLPT聽力原文 問題一 3番

3番&#xff1a;會社で女の人と男の人が話しています。女の人は倉庫に入るとき、どの順番で入口のボタンを押さなければなりませんか。 女&#xff1a;すみません。地下の倉庫に行って、資料を取ってきたいんですが、入口の開け方がわからなくて… 男&#xff1a;ああ、最近、管…

C#/.NET/.NET Core技術前沿周刊 | 第 49 期(2025年8.1-8.10)

前言 C#/.NET/.NET Core技術前沿周刊&#xff0c;你的每周技術指南針&#xff01;記錄、追蹤C#/.NET/.NET Core領域、生態的每周最新、最實用、最有價值的技術文章、社區動態、優質項目和學習資源等。讓你時刻站在技術前沿&#xff0c;助力技術成長與視野拓寬。 歡迎投稿、推薦…

基于強化學習的目標跟蹤 研究初探

強化學習 目標跟蹤Visual tracking by means of deep reinforcement learning and an expert demonstratorYOLO 檢測下基于 ETC-DDPG 算法的無人機視覺跟蹤基于特征與深度強化學習方法的機器人視覺伺服技術研究高性能可拓展視頻目標跟蹤算法研究基于目標運動與外觀特征的多目標…

排序與查找,簡略版

數組的排序 排序的基本介紹 排序是將一組數據&#xff0c;按照一定順序進行排列的過程 排序的分類&#xff1a; 內部排序&#xff1a; 一次性適用數據量小的情況 將需要處理的數據都加載到內部存儲器中進行排序。包括交換式排序&#xff0c;選擇式排序&#xff0c;插入式排序 外…

打靶日常-XSS(反射型和存儲型)

目錄 小皮: 1. 2.這里需要登錄,我們之前爆破出賬號密碼在這里就可以用?編輯 登錄之后:?編輯 使用工具: 先輸入正確字符進行測試:aaa 進行測試: 3.換種控制臺顯示 結果:(使用f12大法) DVWA: 反射型XSS: 低: ?編輯 中:大小寫繞過: ?編輯 也可以雙寫繞過: ?編…

二叉搜索樹深度解析:從原理實現到算法應用----《Hello C++ Wrold!》(18)--(C/C++)

文章目錄前言二叉搜索樹&#xff08;二叉排序樹或二叉查找樹&#xff09;二叉搜索樹的模擬實現二叉搜索樹和有序數組二分查找的比較兩個搜索模型作業部分前言 二叉搜索樹&#xff08;Binary Search Tree&#xff0c;簡稱 BST&#xff09;作為一種重要的樹形數據結構&#xff0…

牛客.空調遙控二分查找牛客.kotori和氣球(數學問題)力扣.二叉樹的最大路徑和牛客.主持人調度(二)

目錄 牛客.空調遙控 二分查找 牛客.kotori和氣球&#xff08;數學問題) 力扣.二叉樹的最大路徑和 牛客.主持人調度(二) 牛客.空調遙控 枚舉n個空調之后&#xff0c;使數組有序&#xff0c;左右下標&#xff0c;用二分查找&#xff0c;然后一個求 長度就好 二分查找 /二分理…

《嵌入式Linux應用編程(二):標準IO高級操作與文件流定位實戰》

今日學習內容1. 行輸入函數安全實踐(1) fgets vs gets函數安全特性換行符處理緩沖區保護fgets指定讀取長度&#xff08;size-1&#xff09;保留\n并添加\0安全&#xff08;防溢出&#xff09;gets無長度限制將\n替換為\0危險2. Linux標準文件流文件流符號設備 標準輸入stdin鍵盤…

Springboot2+vue2+uniapp 小程序端實現搜索聯想自動補全功能

目錄 一、實現目標 1.1 需求 1.2 實現示例圖: 二、實現步驟 2.1 實現方法簡述 2.2 簡單科普 2.3 實現步驟及代碼 一、實現目標 1.1 需求 搜索聯想——自動補全 &#xff08;1&#xff09;實現搜索輸入框&#xff0c;用戶輸入時能顯示模糊匹配結果 &am…

極簡 5 步:Ubuntu+RTX4090 源碼編譯 vLLM

極簡 5 步&#xff1a;UbuntuRTX4090 源碼編譯 vLLM1. 系統依賴&#xff08;一次性&#xff09;2. 進入源碼目錄 & 激活環境3. 啟用 ccache 自動并行度4. 拉代碼 編譯&#xff08;2 行搞定&#xff09;5. 更新 flash-attn&#xff08;與 vLLM 配套&#xff09;6. 啟動 4 …

生產工具革命:定制開發開源AI智能名片S2B2C商城小程序重構商業生態的范式研究

摘要互聯網作為信息工具已深刻改變商業生態&#xff0c;但其本質仍停留在效率優化層面。本文提出&#xff0c;基于定制開發開源AI智能名片與S2B2C商城小程序的深度融合&#xff0c;正在引發生產工具層面的革命性變革。該技術架構通過重構"人-貨-場"關系&#xff0c;實…

Transformer前傳:Seq2Seq與注意力機制Attention

前言 參考了以下大佬的博客 https://blog.csdn.net/v_july_v/article/details/127411638 https://blog.csdn.net/andy_shenzl/article/details/140146699 https://blog.csdn.net/weixin_42475060/article/details/121101749 https://blog.csdn.net/weixin_43334693/article/det…

企業架構工具篇之ArchiMate的HelloWorld(2)

本文通過ArchiMate做一個員工報銷流程設計的小demo,按照步驟都可以做出來,在做這個demo之前先簡單認識下Archimate的開發界面: 模型樹(Models)窗口:通常位于左上方,以樹形結構展示一個或多個 ArchiMate 模型。用戶可在此瀏覽模型的整體結構,快速定位到特定的模型元素,…