2021-1 從檔案構建有向圖API 測試單點和多點可達 c++實現
阿新 • • 發佈:2021-01-23
API
和無向圖Graph.h比較,只改動了幾個地方,一個翻轉圖,新增邊時只是單向新增。
一個改動
返回整個list<int>
可能導致Stack Overflow
,索性只返回一個指向堆的指標。另外,用一個巨集定義簡化書寫。
//原來是
list<int> adj(int v){
return *((*m_adj)[v]);
}
//可以這樣呼叫鄰接點
for(auto &next : G.adj(v)){ .....next; }
//現在改為返回指標
ListPtr Digraph::adj(int v)
{
return (m_adj->at(v));
}
//這樣呼叫
for(auto &next: *G.adj(v)){
next.....;
}
//或者呼叫
ListPtr p=G.adj(v);
for(auto it=p->begin();it!=p->end();++it){
.......(*it);
}
//如果設計一個巨集定義
#define forIt(ListPtr) for(auto it=ListPtr->begin();it!=ListPtr->end();++it)
//最後這樣寫
forIt(G.adj(v)){
........(*it). ...;
}
程式碼
//Digraph.h
#pragma once
#include<iostream>
#include<list>
#include<vector>
#include<fstream>
using namespace std;
#define out(x) cout<<x<<" "
#define hh printf_s("\n")
#define forIt(ListPtr) for(auto it=ListPtr->begin();it!=ListPtr->end();++it)
/*******************如無必要,勿增實體************************/
typedef list<int>* ListPtr;
class Digraph
{
public:
Digraph(int V);
Digraph(string file);
void addEdge(int v, int w);
ListPtr adj(int v);
int V() { return m_V; }
int E() { return m_E; }
Digraph reverse();
string toString();
private:
int m_V=0;
int m_E=0;
vector<ListPtr>* m_adj=nullptr;
void initGraph(int V);
bool isParallel_edges_self_loop(int v, int w);
string intToStr(int n);
};
void testDigraph();
#include "Digraph.h"
Digraph::Digraph(int V)
{
m_V = V;
m_E = 0;
m_adj = new vector<ListPtr>(V, nullptr);
for (int i = 0; i < V; i++)
{
m_adj->at(i) = new list<int>(0);
}
}
Digraph::Digraph(string file)
{
ifstream in(file, ios::in);
if (!in) {
printf_s("file open error.");
return;
}
int V, E;
in >> V;
initGraph(V);
in >> E;//不一定等於最後的邊數,因為可能不包含自環和平行邊
int w, v;
for (int i = 0; i < E; i++)
{
in >> w >> v;
addEdge(w, v);
}
in.close();
}
void Digraph::addEdge(int v, int w)
{
//if (isParallel_edges_self_loop(v, w)) return;
(*m_adj)[v]->push_front(w);
m_E++;
}
ListPtr Digraph::adj(int v)
{
return (m_adj->at(v));
}
Digraph Digraph::reverse()
{
Digraph* rG = new Digraph(m_V);
for (int i = 0; i < m_V; ++i) {
forIt(m_adj->at(i)) {
rG->addEdge(*it, i);
}
}
return *rG;
}
string Digraph::toString()
{
string s = intToStr(m_V) + " vertices, " + intToStr(m_E) + " edges\n";
for (int i = 0; i < m_V; i++)
{
s += intToStr(i) + ": ";
for (auto adj : *(m_adj->at(i)))
{
s += intToStr(adj) + " ";
}
s += "\n";
}
return s;
}
string
inline Digraph::intToStr(int n)
{
char int_s[20] = { 0 };//末尾加上'\0'
_itoa_s(n, int_s, 10);// 10 表示十進位制
return string(int_s);
}
void
inline Digraph::initGraph(int V)
{
m_V = V;
m_E = 0;
m_adj = new vector<ListPtr>(V, nullptr);
for (int i = 0; i < V; i++)
{
m_adj->at(i) = new list<int>(0);
}
}
inline
bool Digraph::isParallel_edges_self_loop(int v, int w)
{
if (v == w) return true;
for (auto& n : *(m_adj->at(v))) {
if (n == w) {
return true;
}
}
return false;
}
void testDigraph()
{
Digraph dG("tinyDG.txt");
out(dG.toString()), hh;
Digraph rG=dG.reverse();
out(rG.toString()),hh;
}
tinyDG.txt
13
22
4 2
2 3
3 2
6 0
0 1
2 0
11 12
12 9
9 10
9 11
7 9
10 12
11 4
4 3
3 5
6 8
8 6
5 4
0 5
6 4
6 9
7 6
DFS搜尋可達性
#pragma once
#include"Digraph.h"
class DirectedDFS
{
public:
DirectedDFS(Digraph& G,int s);//搜尋單點可達
DirectedDFS(Digraph& G, list<int> sources);//標記多點可達的點
bool marked(int v);
private:
vector<bool>* m_marked = nullptr;
void dfs(Digraph& G, int s);
};
void testDFS();
#include "DirectedDFS.h"
DirectedDFS::DirectedDFS(Digraph& G, int s)
{
m_marked = new vector<bool>(G.V(), false);
dfs(G, s);
}
DirectedDFS::DirectedDFS(Digraph& G, list<int> sources)
{
m_marked = new vector<bool>(G.V(), false);
for (auto& s : sources) {
if (!m_marked->at(s)) {
dfs(G, s);
}
}
}
bool DirectedDFS::marked(int v)
{
return m_marked->at(v);
}
void DirectedDFS::dfs(Digraph& G, int s)
{
m_marked->at(s) = true;
forIt (G.adj(s)) {
if (!m_marked->at(*it)) {
dfs(G, *it);
}
}
}
void testDFS()
{
Digraph G("tinyDG.txt");
list<int> sources = { 2,6,8 };
DirectedDFS dfs(G, sources);
out("can reach :");
for (int i = 0; i < G.V(); ++i) {
if (dfs.marked(i)) {
out(i);
}
}
hh;
}