图的存储结构:
1.邻接矩阵:
const int maxnum = 10001;
int graph1[maxnum][maxnum];
void build(int n) {//创建邻接矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
graph1[i][j] = 0;
}
}
}
void insert(vector<vector<int>>& edges) {//加入边
for (auto i : edges) {
graph1[i[0]][i[1]] = i[2];
//graph1[i[1]][i[0]] = i[2];创建无向图时要加入
}
}
void travel(int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << i << " " << j << " " << graph1[i][j] << endl;
}
}
}
邻接矩阵用一个n*n的二维矩阵记录点和点的之间边的权值,如果是无向图要对称加入。缺点是如果点的数量过大,开辟的数组会太大,浪费空间
2.邻接表:
vector<vector<vector<int>>>graph2;
void build(int n) {//创建邻接表
graph2.clear();
for (int i = 0; i < n; i++) {
graph2.push_back(vector<vector<int>>());//每行加入一个空向量
}
}
void insert(vector<vector<int>>& edges) {
for (auto i : edges) {
graph2[i[0]].push_back({ i[1],i[2] });//加入节点
//graph2[i[1]].push_back({ i[0],i[2] });
}
}
void travel(int n) {
for (int i = 0; i<n; i++) {
for (auto j : graph2[i]) {
cout << i << " " << j[0] << " " << j[1] << endl;
}
}
}
邻接表用动态存储结构存储
3.链式前向星:
const int maxnum = 10001;//比边的数量大
int head[maxnum];//头部边
int next[maxnum];//下一条边
int to[maxnum];//边去的点
int value[maxnum];
int cnt;
void build(int n){//为了方便从1节点开始用
cnt = 1;
for (int i = 1; i <= n; i++)
head[i] = 0;
}
void addedge(int u, int v, int w) {
next[cnt] = head[u];
to[cnt] = v;
value[cnt] = w;
head[u] = cnt++;
}
void insert(vector<vector<int>>& edges) {
for (auto i : edges) {
addedge(i[0], i[1], i[2]);
//addedge(i[1], i[0], i[2]);
}
}
void travel(int n) {
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j > 0; j = next[j]) {
cout << i << " " << to[j] << " " << value[j] << endl;
}
}
}
链式前向星只需要用比边的数量大的几个一维数组即可
拓扑排序:如果一个任务是由几个小任务按顺序完成的,拓扑排序就是将每个小任务按完成的先后顺序排出一个有效的顺序(顺序可能是不唯一的)。也就是如果可以拓扑排序则说小任务之间形成一个有向的无环图
最经典的拓扑排序法----入度删除法:统计所有点的入度,找到入度为0的点,从这个点开始依次删除入度为0的点并消除对其他点的入度影响,重复操作直到结束,如果统计到的点和总的点的个数不一样,说明这个图有环
邻接表存储
class Solution {
public:
vector<vector<int>>graph;
vector<int>indegree;
void build(int n){
indegree.assign(n,0);
for(int i=0;i<n;i++)
graph.push_back(vector<int>());
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
build(numCourses);
for(auto edge:prerequisites){
graph[edge[1]].push_back(edge[0]);
indegree[edge[0]]++;
}
vector<int>queue(numCourses,0);
int l=0,r=0;
for(int i=0;i<numCourses;i++)
if(indegree[i]==0)
queue[r++]=i;
int cnt=0;
while(l<r){
int cur=queue[l++];
cnt++;
for(auto i:graph[cur]){
indegree[i]--;
if(indegree[i]==0)
queue[r++]=i;
}
}
return cnt==numCourses?queue:vector<int>();
}
};
链式前向星存储:
class Solution {
public:
static const int maxnum = 4000001;
int head[2001];
int next[maxnum];
int to[maxnum];
int cnt;
int indegree[maxnum];
void build(int n) {
cnt = 1;
for (int i = 0; i < n; i++) {
head[i] = 0;
indegree[i] = 0;
}
}
void addage(int u, int v) {
next[cnt] = head[u];
to[cnt] = v;
head[u] = cnt++;
}
void insert(vector<vector<int>>& prerequisites) {
for (auto edge : prerequisites) {
addage(edge[1], edge[0]);
indegree[edge[0]]++;
}
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
build(numCourses);
insert(prerequisites);
vector<int> queue(numCourses, 0);
int l = 0, r = 0;
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0)
queue[r++] = i;
}
int cnt = 0;
while (l < r) {
int cur = queue[l++];
cnt++;
for (int edge = head[cur]; edge > 0; edge = next[edge]) {
int ne = to[edge];
indegree[ne]--;
if (indegree[ne] == 0)
queue[r++] = ne;
}
}
return cnt == numCourses ? queue : vector<int>();
}
};
#include<iostream>
#include <vector>
using namespace std;
const int maxnum = 100001;
int head[maxnum];
int nex[maxnum];
int to[maxnum];
int indegree[maxnum];
int cnt;
void swap(vector<int>&nums,int i, int j) {
int rem = nums[i];
nums[i] = nums[j];
nums[j] = rem;
}
void build(int n) {
for (int i = 1; i <= n; i++) {
head[i] = 0;
indegree[i] = 0;
}
cnt = 1;
}
void addedge(int u, int v) {
nex[cnt] = head[u];
to[cnt] = v;
head[u] = cnt++;
indegree[v]++;
}
void heapinsert(vector<int>& nums, int i) {
while (nums[i] < nums[(i - 1) / 2]) {
swap(nums, i,(i - 1) / 2);
i = (i - 1) / 2;
}
}
void heapify(vector<int>& nums, int i, int size) {
int l = 2 * i + 1;
while (l < size) {
int best = l + 1 < size && nums[l + 1] <= nums[l] ? l + 1 : l;
best = nums[i] <= nums[best] ? i : best;
if (best == i) break;
swap(nums, i, best);
i = best;
l = 2 * i + 1;
}
}
int main() {
int n, m;
cin >> n >> m;
build(n);
while (m--) {
int u, v;
cin >> u >> v;
addedge(u, v);
}
vector<int>queue(n+1,0);
int l = 0, r = 0;
for (int i = 1; i <= n; i++)
if (indegree[i] == 0) {
queue[r++] = i;
heapinsert(queue, r - 1);//建堆
}
vector<int>ans;
while (l<r) {
int cur = queue[l];
ans.push_back(cur);
queue[l] = queue[--r];//将堆的最后一个元素放到0位置上
heapify(queue,l,r);//向下调整
for (int edge = head[cur]; edge != 0; edge = nex[edge]) {
int x = to[edge];
if (--indegree[x] == 0){
queue[r++]=x;
heapinsert(queue,r-1);//加入堆
}
}
}
for (int i : ans)
cout << i << " ";
return 0;
}
将数据存储在图中按拓扑排序输出,但此题要求按字典序输出,所以不能用队列接收,要用堆来接收
class Solution {
public:
static const int maxnum = 30;
vector<vector<int>> graph;
int indegree[maxnum];
void build() {
for (int i = 0; i < 26; i++) {
graph.push_back(vector<int>());
indegree[i] = -1;
}
}
void insert(int u, int v) {
graph[u].push_back(v);
indegree[v]++;
}
int get_index(char c) { return c - 'a'; }
string alienOrder(vector<string>& words) {
build();
int n = words.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < words[i].size(); j++)
indegree[get_index(words[i][j])] = 0;//为了处理相邻的两个单词是相同的情况
} //不然会漏掉一些字母
for (int i = 0; i < n - 1; i++) {
string a = words[i];
string b = words[i + 1];
int len = min(a.size(), b.size());
for (int j = 0; j < len; j++) {
if (a[j] != b[j]) {
int x = get_index(a[j]);
int y = get_index(b[j]);
insert(x, y);
break;
}
if (j == len - 1 && a[j] == b[j] && a.size() > b.size())
return "";
}
}
string ans;
int l = 0, r = 0;
int kinds = 0;
int queue[30] = {0};
for (int i = 0; i < 26; i++) {
if (indegree[i] == 0)
queue[r++] = i;
if (indegree[i] != -1)
kinds++;
}
while (l < r) {
int cur = queue[l++];
ans += (cur + 'a');
for (int i = 0; i < graph[cur].size(); i++) {
int path = graph[cur][i];
if (--indegree[path] == 0)
queue[r++] = path;
}
}
for (int i = 0; i < 26; i++)
if (indegree[i] > 0)
return "";
return ans;
}
};
class Solution {
public:
static const int maxnum = 1001;
vector<vector<int>> graph;
int indegree[maxnum];//每stamp.size()长度的字串错误的位置数量作为这个字串的度
void swap(vector<int>& nums, int i, int j) {
int rem = nums[i];
nums[i] = nums[j];
nums[j] = rem;
}
void build(int n, int m) {
int size = n - m;
for (int i = 0; i <= size; i++) {
indegree[i] = m;
}
for (int i = 0; i < n + 2; i++)
graph.push_back(vector<int>());
}
void insert(int u, int v) { graph[u].push_back(v); }
vector<int> movesToStamp(string stamp, string target) {
int n = target.size();
int m = stamp.size();
if (n < m)
return vector<int>();
build(n, m);
int queue[n - m + 1];
int l = 0, r = 0;
for (int i = 0; i <= n - m; i++) {
for (int j = 0; j < m; j++) {
if (stamp[j] == target[i + j]) {
if (--indegree[i] == 0)
queue[r++] = i;
} else {
insert(i + j, i);
}
}
}
vector<int> path;
vector<bool> visit(n + 1, false);//记录哪些位置正确了
int size = 0;
while (l < r) {
int cur = queue[l++];
path.push_back(cur);
size++;
for (int i = 0; i < m; i++) {
if (!visit[cur + i]) {
visit[cur + i] = true;
int x = cur + i;
for (int j = 0; j < graph[x].size(); j++) {
if (--indegree[graph[x][j]] == 0)
queue[r++] = graph[x][j];
}
}
}
}
if (size != n - m + 1) {
return vector<int>();
}
l = 0, r = size - 1;
while (l < r) {
swap(path, l++, r--);
}
return path;
}
};
此题就比较难了。首先这个问题正着推是较难得出规律的,我们不妨反着推:target如果可以被stamp盖出,那么target一定在某几块位置上的字串是和stamp相同(我们把最后盖的这些字串叫做A),那么和这些位置有重叠的位置上的字串(也就是在A之前盖的字串,叫做B)错误的位置就是最后盖的这些字串给修改正确的,那么再之前的字串C是被B修改正确的,所以盖的字串之间就由覆盖的位置联系起来了,他们可以按照完成的顺序依次逆推回去。最后把收集到的答案倒着重新排序即可
拓扑排序在上述基础的用法外,还可以在遍历节点的同时可以从上游推送信息向下游节点
#include <iostream>
#include <vector>
using namespace std;
const int mod = 80112002;
const int maxnum = 5002;
vector<vector<int>>pragh;
int indegree[maxnum];
int path[maxnum];//记录遍历到节点时遍历过的路径数
void build(int n) {
for (int i = 0; i <= n+1; i++) {
pragh.push_back(vector<int>());
indegree[i] = 0;
}
}
void insert(int u, int v) {
pragh[u].push_back(v);
indegree[v]++;
}
int main() {
int n, m;
cin >> n >> m;
build(n);
while (m--) {
int u, v;
cin >> u >> v;
insert(u, v);
}
int queue[maxnum];
int l = 0, r = 0;
for (int i = 1; i <= n; i++)
if (indegree[i] == 0) {
queue[r++] = i;
path[i] = 1;//开始的节点path设置为1
}
int ans=0;
while (l < r) {
int cur = queue[l++];
if(pragh[cur].size()==0)
ans=(ans+path[cur])%mod;//在结尾节点处计数
for (int i : pragh[cur]) {
path[i] = (path[i] % mod + path[cur] % mod) % mod;//cur处的路径数传递给i节点
if (--indegree[i] == 0) {
queue[r++] = i;
}
}
}
cout << ans%mod;
return 0;
}
大流程和拓扑排序一样,只不过为了最后能得到食物链数,设置一个path数组来记录每遍历到一个节点从开头到现在一共遍历了多少条路径,依次传递下去
class Solution {
public:
static const int maxnum = 501;
vector<vector<int>> pragh;
int indegree[maxnum];
vector<int> path;
void build(int n) {
for (int i = 0; i < n; i++) {
pragh.push_back(vector<int>());
indegree[i] = 0;
path.push_back(i);
}
}
void insert(int u, int v) {
pragh[u].push_back(v);
indegree[v]++;
}
vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& quiet) {
int n = quiet.size();
build(n);
for (auto i : richer) {
insert(i[0], i[1]);
}
int queue[maxnum];
int l = 0, r = 0;
for (int i = 0; i < n; i++) {
if (indegree[i] == 0)
queue[r++] = i;
}
vector<int> ans(n, 0);
while (l < r) {
int cur = queue[l++];
ans[cur] = path[cur];
for (int i : pragh[cur]) {
if (quiet[path[cur]] <= quiet[path[i]])
path[i] = path[cur];
if (--indegree[i] == 0)
queue[r++] = i;
}
}
return ans;
}
};
class Solution {
public:
static const int maxnum = 50005;
vector<vector<int>> pragh;
int indegree[maxnum];
int path[maxnum];
void build(int n) {
for (int i = 0; i <= n + 1; i++) {
pragh.push_back(vector<int>());
indegree[i] = 0;
path[i] = 0;
}
}
void insert(int u, int v) {
pragh[u].push_back(v);
indegree[v]++;
}
int minimumTime(int n, vector<vector<int>>& relations, vector<int>& time) {
build(n);
for (auto edges : relations) {
insert(edges[0], edges[1]);
}
int queue[n + 2];
int l = 0, r = 0;
for (int i = 1; i <= n; i++)
if (indegree[i] == 0) {
queue[r++] = i;
path[i] = time[i - 1];
}
int ans = 0;
while (l < r) {
int cur = queue[l++];
if (pragh[cur].size() == 0)
ans = max(ans, path[cur]);
for (auto i : pragh[cur]) {
path[i] = max(path[i], path[cur]);
if (--indegree[i] == 0) {
queue[r++] = i;
path[i] += time[i - 1];
}
}
}
return ans;
}
};
此题涉及基环树结构
class Solution {
public:
int maximumInvitations(vector<int>& favorite) {
const int n = favorite.size();
int indegree[n];
int path[n];
int small = 0;
int big = 0;
int queue[n + 1];
int l = 0, r = 0;
for (int i = 0; i < n; i++) {
indegree[i] = 0;
path[i] = 0;
}
for (int i = 0; i < n; i++)
indegree[favorite[i]]++;
for (int i = 0; i < n; i++) {
if (indegree[i] == 0) {
queue[r++] = i;
}
}
while (l < r) {
int cur = queue[l++];
path[favorite[cur]] = max(path[favorite[cur]], path[cur] + 1);
if (--indegree[favorite[cur]] == 0)
queue[r++] = favorite[cur];
}
for (int i = 0; i < n; i++) {
int size = 0;
if (indegree[i] != 0) {
size++;
indegree[i]--;
for (int j = favorite[i]; j != i; j = favorite[j]) {
indegree[j]--;
size++;
}
}
if (size == 2)
small += (2 + path[i] + path[favorite[i]]);
if (size > 2)
big = max(big, size);
}
return max(big, small);
}
};