1. 程式人生 > 其它 >記錄向——圖

記錄向——圖

技術標籤:c、c++、資料結構與演算法演算法c++

文章目錄

並查集

最先接觸到並查集是在克魯斯卡爾演算法中,構建最小生成樹。並查集在這裡最核心的作用就是看有沒有環。最核心的幾句程式碼就是:

//首先初始化parents[i]=i
//再定義查詢函式
int find(int *parents, int f){
	while(parents[f]!=f){
		f=parents[f];
	}
	return f;
}
//當分別從兩個點出發,查詢的結果相同說明構成迴路,否則就是出現一條可以走的路徑
n=find(parents,a)
m=find(parents,b)
if(n!=m){ parents[n]=m; }

leetcode 1202:https://leetcode-cn.com/problems/smallest-string-with-swaps/

上面這個題目想到了使用並查集,但是發現不只是一個生成樹的,而且生成樹之間可能交叉輸出。

  1. 如何構建多個生成樹:不同點輸入找到的根是不同的,以此存入不同的map中。
class DisjointSetUnion {
private:
    // n表示長度,f表示並查集陣列
    // rank是想每一個生成樹的根節點相同,rank儲存子樹節點個數
    vector<int> f, rank;
int n; public: DisjointSetUnion(int _n) { n = _n; rank.resize(n, 1); f.resize(n); for (int i = 0; i < n; i++) { f[i] = i; } } int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); } //可以一起排序的放在一起 void unionSet
(int x, int y) { int fx = find(x), fy = find(y); //相同說明之前有了,不需要再合併了 if (fx == fy) { return; } //交換fx與fy變數的內容,fx將代表rank大的一方 if (rank[fx] < rank[fy]) { swap(fx, fy); } rank[fx] += rank[fy]; //更新並查集 f[fy] = fx; } }; DisjointSetUnion dsu(s.length()); for (auto& it : pairs) { dsu.unionSet(it[0], it[1]); } unordered_map<int, vector<int>> mp; for (int i = 0; i < s.length(); i++) { mp[dsu.find(i)].emplace_back(s[i]); //push_back }
  1. 確定生成樹之間的優先關係。
for (auto& [x, vec] : mp) {
    sort(vec.begin(), vec.end(), greater<int>());//從大到小排序
}
// i號位置的所在的生成樹,輸出其中最小的,結果多個生成樹之間可能交叉。
for (int i = 0; i < s.length(); i++) {
    int x = dsu.find(i);
    s[i] = mp[x].back();//從後面一個個出
    mp[x].pop_back();
}

拓撲排序

leetcod 1203:https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies/submissions/

看出了拓撲排序,但是重點在於:對於拓撲排序需要的入度關係如何表達沒有理清楚,而且這道題也不是一個小組內,還有多個小組之間的拓撲關係。

  1. 組間與組內的入度關係表達;
vector<vector<int>> groupItem(n + m);   //某組負責哪些專案

// 組間和組內依賴圖
vector<vector<int>> groupGraph(n + m);
vector<vector<int>> itemGraph(n);

// 組間和組內入度陣列
vector<int> groupDegree(n + m, 0);
vector<int> itemDegree(n, 0);
 
vector<int> id;
for (int i = 0; i < n + m; ++i) {
    id.emplace_back(i);
}

int leftId = m;
// 給未分配的 item 分配一個 groupId
for (int i = 0; i < n; ++i) {
    if (group[i] == -1) {
        group[i] = leftId;
        leftId += 1;
    }
    groupItem[group[i]].emplace_back(i);
}
// 依賴關係建圖
for (int i = 0; i < n; ++i) {
    int curGroupId = group[i];
    for (auto& item: beforeItems[i]) {
        int beforeGroupId = group[item];
        if (beforeGroupId == curGroupId) {  //這個專案之前的專案的負責組是否和這個專案的負責組是同一個組。
            itemDegree[i] += 1;                 // 這個專案入度+1
            itemGraph[item].emplace_back(i);   //之前專案出去的邊
        } else {
            groupDegree[curGroupId] += 1;
            groupGraph[beforeGroupId].emplace_back(curGroupId);
        }
    }
}
  1. 解決組間拓撲問題;
  2. 解決組內拓撲問題。