首页 > 其他分享 >拓扑排序

拓扑排序

时间:2024-09-12 17:03:01浏览次数:11  
标签:degree item int 拓扑 入度 ++ vector 排序

拓扑排序

LCR 113. 课程表 II

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>> &prerequisites) {
        vector<int> res;
        // 入度
        vector<int> in_degree(numCourses, 0);
        // 邻接表
        vector<vector<int>> graph(numCourses);
        // 初始化邻接表和入度表
        for (const auto &item: prerequisites) {
            graph[item[1]].emplace_back(item[0]);
            in_degree[item[0]]++;
        }

        // 存放入度为 0 的顶点
        queue<int> q;
        for (int i = 0; i < numCourses; ++i)
            if (in_degree[i] == 0) q.emplace(i);

        // 已经加入拓扑序列的节点个数
        int count = 0;
        while (!q.empty()) {
            // 取出入度为 0 的顶点
            int node = q.front();
            q.pop();
            // 加入拓扑序列
            res.emplace_back(node);
            count++;

            // 遍历邻接表,把入度为 0 的节点从图中断开
            for (const auto &next: graph[node]){
                // 下个顶点的入度减一
                in_degree[next]--;
                // 出现入度为 0 的节点,加入队列
                if (in_degree[next] == 0) q.emplace(next);
            }
        }

        return count == numCourses ? res : vector<int>();
    }
};

AB13 【模板】拓扑排序

#include <iostream>
#include <vector>
#include <queue>

using namespace std;


int main() {
    int n, m;
    cin >> n >> m;
    vector<pair<int, int>> edges(m);
    for (int i = 0; i < m; ++i)
        cin >> edges[i].first >> edges[i].second;

    // 顶点编号从 1 开始
    // 邻接矩阵
    vector<vector<int>> graph(n + 1);
    // 记录入度
    vector<int> in_degree(n + 1, 0);
    for (const auto &edge: edges) {
        graph[edge.first].emplace_back(edge.second);
        in_degree[edge.second]++;
    }

    // 存放入度为 0 的顶点
    queue<int> q;
    for (int i = 1; i <= n; ++i)
        if (in_degree[i] == 0)
            q.emplace(i);

    vector<int> res(n);
    int count = 0;
    // 依次从图中取出入度为 0 的节点
    while (!q.empty()) {
        int node = q.front();
        q.pop();
        res[count++] = node;
        // 遍历指向的顶点,将其入度减一
        for (int i = 0; i < graph[node].size(); ++i) {
            // 下个顶点
            int next = graph[node][i];
            in_degree[next]--;
            if (in_degree[next] == 0) q.emplace(next);
        }
    }

    if (count == n) {
        for (int i = 0; i < n - 1; ++i)
            cout << res[i] << " ";
        cout << res[n - 1];
    } else {
        cout << -1;
    }
}

U107394 拓扑排序模板

  • 有向无环图上有n个点,m条边。求这张图字典序最小的拓扑排序的结果。字典序最小指希望排好序的结果中,比较靠前的数字尽可能小。
  • 把存放入度为 0 的队列换成小顶堆即可按字典序输出
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<pair<int, int>> edges(m);
    for (int i = 0; i < m; ++i)
        cin >> edges[i].first >> edges[i].second;

    // 顶点编号从 1 开始
    // 邻接矩阵
    vector<vector<int>> graph(n + 1);
    // 记录入度
    vector<int> in_degree(n + 1, 0);
    for (const auto &edge: edges) {
        graph[edge.first].emplace_back(edge.second);
        in_degree[edge.second]++;
    }

    // 存放入度为 0 的顶点
    priority_queue<int, vector<int>, greater<>> q;
    for (int i = 1; i <= n; ++i)
        if (in_degree[i] == 0)
            q.push(i);

    vector<int> res(n);
    int count = 0;
    // 依次从图中取出入度为 0 的节点
    while (!q.empty()) {
        int node = q.top();
        q.pop();
        res[count++] = node;
        // 遍历指向的顶点,将其入度减一
        for (int i = 0; i < graph[node].size(); ++i) {
            // 下个顶点
            int next = graph[node][i];
            in_degree[next]--;
            if (in_degree[next] == 0) q.push(next);
        }
    }

    if (count == n) {
        for (int i = 0; i < n - 1; ++i)
            cout << res[i] << " ";
        cout << res[n - 1];
    } else {
        cout << -1;
    }
}
#include <iostream>
#include <vector>

using namespace std;

// 链式前向星
vector<int> head;
vector<int> nxt;
vector<int> to;
int edgeIndex = 0;
vector<int> in_degree;

void addEdge(int u, int v) {
    edgeIndex++;
    nxt[edgeIndex] = head[u];
    to[edgeIndex] = v;
    head[u] = edgeIndex;
}

// 顶点编号和边的编号从 1 开始
void buildGraph(int vertexNum, int edgeNum, vector<pair<int, int>> &edges) {
    head.resize(vertexNum, 0);
    nxt.resize(edgeNum);
    to.resize(edgeNum);
    for (const auto &item: edges)
        addEdge(item.first, item.second);
}

// 小顶堆存放入度为 0 的顶点编号
vector<int> heap;
int lenOfHeap;

void buildHeap(int max) {
    heap.resize(max);
    lenOfHeap = 0;
}

// 下标从 0 开始
void adjustHeap(int curIndex) {
    int val = heap[curIndex];
    int leftChild = 2 * curIndex + 1;
    while (leftChild <= (lenOfHeap - 1)) {
        // 右孩子更小就选右孩子
        if ((leftChild < (lenOfHeap - 1))
            && heap[leftChild] > heap[leftChild + 1])
            leftChild++;
        // 不需要调整
        if (heap[leftChild] >= val) break;
        // 调整
        heap[curIndex] = heap[leftChild];
        curIndex = leftChild;
        leftChild = 2 * curIndex + 1;
    }
    heap[curIndex] = val;
}

void push(int val) {
    int curIndex = lenOfHeap;
    // 堆大小加一
    lenOfHeap++;
    // 从下往上调整到合适位置
    int parentIndex = (curIndex - 1) >> 1;

    while (parentIndex >= 0 && heap[parentIndex] > val) {
        // 父节点下移
        heap[curIndex] = heap[parentIndex];
        curIndex = parentIndex;
        parentIndex = (curIndex - 1) >> 1;
    }

    heap[curIndex] = val;
}

int getTop() {
    int res = heap[0];
    // 末尾元素移到堆顶
    heap[0] = heap[lenOfHeap - 1];
    lenOfHeap--;
    // 调整堆顶
    adjustHeap(0);
    return res;
}


int main() {
    int n, m;
    cin >> n >> m;
    vector<pair<int, int>> edges(m);
    for (int i = 0; i < m; ++i)
        cin >> edges[i].first >> edges[i].second;

    buildGraph(n + 1, m + 1, edges);
    buildHeap(n + 1);

    // 记录入度
    in_degree.resize(n + 1, 0);
    for (const auto &item: edges)
        in_degree[item.second]++;

    // 初始状态入度为 0 的点加入堆
    for (int i = 1; i < in_degree.size(); ++i)
        if (in_degree[i] == 0) push(i);

    vector<int> res(n);
    int resIndex = 0;
    // 取出入度为 0 的点,并删除关联的边
    while (lenOfHeap != 0) {
        int v = getTop();
        res[resIndex++] = v;
        // 把所有关联的顶点入度减一
        int e = head[v];
        while (e > 0) {
            in_degree[to[e]]--;
            if (in_degree[to[e]] == 0) push(to[e]);
            e = nxt[e];
        }
    }

    if (resIndex == n) {
        for (int i = 0; i < n - 1; ++i)
            cout << res[i] << " ";
        cout << res[n - 1];
    } else {
        cout << -1;
    }
}

LCR 114. 火星词典

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

class Solution {
public:
    string alienOrder(vector<string> &words) {

        vector<int> in_degree(26, -1);
        // 入度为 -1 的就是没出现过的字符
        for (const auto &str: words)
            for (int i = 0; i < str.length(); ++i)
                in_degree[str[i] - 'a'] = 0;

        // 建图
        vector<vector<int>> graph(26);
        for (int i = 0; i < words.size() - 1; ++i) {
            string cur = words[i];
            string nxt = words[i + 1];
            int j = 0;
            int len = min(cur.length(), nxt.length());
            while (j < len) {
                if (cur[j] != nxt[j]) {
                    // cur[j] 字典序在 nxt[j] 前
                    graph[cur[j] - 'a'].emplace_back(nxt[j] - 'a');
                    in_degree[nxt[j] - 'a']++;
                    break;
                }
                j++;
            }
            // 前面相同,但 nxt 长度更小,没有字典序
            if (j < cur.length() && j == nxt.length()) return "";
        }

        queue<int> q;
        // 字符种类数
        int kinds = 0;
        for (int i = 0; i < 26; ++i) {
            if (in_degree[i] != -1) kinds++;
            if (in_degree[i] == 0) q.push(i);
        }

        string res = "";
        while (!q.empty()) {
            int cur = q.front();
            q.pop();
            // 追加一个字符 cur
            res.append(1, cur + 'a');
            for (const auto &item: graph[cur]) {
                in_degree[item]--;
                if (in_degree[item] == 0) q.push(item);
            }
        }
        return res.length() == kinds ? res : "";
    }
};

936. 戳印序列

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

class Solution {
public:
    vector<int> movesToStamp(string stamp, string target) {
        int m = stamp.length();
        int n = target.length();
        // 初始时,入度就是以 i 位置开始,匹配不上的位置的总数,为 m
        vector<int> in_degree(n - m + 1, m);

        vector<vector<int>> graph(n);
        queue<int> q;
        // 以 i 开头长度为 m 的字符串与印章对比
        for (int i = 0; i <= n - m; ++i) {
            for (int j = 0; j < m; ++j) {
                if (target[i + j] == stamp[j]) {
                    // 匹配上的位置,入度减一
                    in_degree[i]--;
                    // 入度为 0,入队
                    if (in_degree[i] == 0) q.push(i);
                } else {
                    // 匹配不上的位置 -> 开头下标 i
                    graph[i + j].emplace_back(i);
                }
            }
        }
        // 同一个位置取消错误不重复统计
        vector<bool> visited(n);
        vector<int> res(n - m + 1);
        int size = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            // 入度为 0 的最后盖章
            res[size++] = v;
            // 以 v 下标开始盖章
            for (int i = 0; i < m; ++i) {
                // 这个位置可以由后面的某次盖章处理掉
                if (visited[v + i] == true) continue;
                // 把仍有错误的位置盖上,变成 true
                visited[v + i] = true;
                // v + i 位置盖上后,找到所有在 v + i 匹配不上的开头下标 item
                // 将 item 开头的位置,入度减小一,表示 v + i 位置匹配上了
                for (const auto &item: graph[v + i]) {
                    in_degree[item]--;
                    // 如果以 item 开头的位置,后面已经全都匹配上了,也就是入度为 0 了,就入队
                    if (in_degree[item] == 0) q.push(item);
                }
            }
        }
        if (size != n - m + 1) return vector<int>();
        // 反转,先加入结果的是最后盖的
        reverse(begin(res), end(res));
        return res;
    }
};

标签:degree,item,int,拓扑,入度,++,vector,排序
From: https://www.cnblogs.com/sprinining/p/18410625

相关文章

  • 【每日一题】LeetCode 2576.求出最多标记下标(贪心、数组、双指针、二分查找、排序)
    【每日一题】LeetCode2576.求出最多标记下标(贪心、数组、双指针、二分查找、排序)题目描述给定一个整数数组nums,数组下标从0开始。你需要执行以下操作,直到无法再执行为止:选择两个互不相同且未标记的下标i和j。满足条件2*nums[i]<=nums[j],则标记下标i和j。......
  • 排序算法
    排序算法是计算机科学中的基本算法之一,旨在将一组数据按照特定顺序(通常是升序或降序)排列。以下是一些常见的排序算法及其特点:1.冒泡排序(BubbleSort)描述:通过重复遍历待排序的数组,比较相邻的元素并交换它们的位置,直到没有需要交换的元素为止。时间复杂度:最坏和平均情况下为......
  • 爬楼梯(LeetCode)&冒泡和选择排序
    目录一、爬楼梯(LeetCode)1、题目2、代码二、冒泡排序三、选择排序一、爬楼梯(LeetCode)1、题目2、代码二、冒泡排序三、选择排序......
  • 十大排序算法的特点及应用场景
    一.十大经典排序算法介绍1.冒泡排序(BubbleSort)原理:通过重复遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素为止。特点:简单但效率低,适合小数据量的排序。2.选择排序(SelectionSort)原理:首......
  • 复习排序算法
    备战acm校队第二天。实惨,排序学了好几年了,还是没有熟练,意难平啊。特此打开oi-wiki,从头到尾把排序敲一遍。选择排序比较简单。就是把数组里最大数找出来放到最后,然后再把第二大的找出来放在倒数第二位,依次类推,排序就结束了。注:TMD,Dev-C++原来不能把中文注释在代码,不然拷贝出来......
  • 冒泡排序理解
    1.1思路冒泡排序是一种简单的排序方法。基本思路是通过两两比较相邻的元素并交换它们的位置,从而使整个序列按照顺序排列。该算法一趟排序后,最大值总是会移到数组的最后面,那么接下来就不用再考虑这个最大值。一直重复这样的操作,最终就可以得到排序完成的数组。这种算法是稳......
  • Java 排序算法详解
    排序是计算机科学中的基本操作,Java提供了多种排序算法来满足不同的需求。常见的排序算法包括冒泡排序、选择排序、插入排序、归并排序、快速排序和堆排序。本文将逐一介绍这些排序算法及其Java实现。1.冒泡排序(BubbleSort)冒泡排序是一种简单的排序算法,其基本思想是......
  • 洛谷题单指南-分治与倍增-P1177 【模板】归并排序
    原题链接:https://www.luogu.com.cn/problem/P1177题意解读:归并排序模版题。解题思路:第一步:确定分界点。mid=(l+r)/2第二步:排序。对左右两边递归排序第三步:归并。合并左右两边排序好的内容关键在第三步:通过双指针对两个有序数组进行合并100分代码:#include<bits/std......
  • JAVA中的八大排序 可视化精华模板 (思路+代码实践)
    “批判他人总是想的太简单剖析自己总是想的太困难”文章目录前言文章有误敬请斧正不胜感恩!1.冒泡排序(时间复杂度o(n^2))概念步骤可视化代码实现2.选择排序(时间复杂度o(n^2))概念步骤可视化代码实现3.插入排序(时间复杂度o(n^2))概念步骤可视化代码示例4.快速排序(时间......
  • c语言--力扣简单题目(删除排序链表中的重复元素)讲解
    题目如下:给定一个已排序的链表的头head,删除所有重复的元素,使每个元素只出现一次。返回已排序的链表。示例1:输入:head=[1,1,2]输出:[1,2]示例2:输入:head=[1,1,2,3,3]输出:[1,2,3]提示:链表中节点数目在范围[0,300]内-100<=Node.val<=100题目数据保......