C++ でのグラフの簡潔で正確な隣接リスト表現を探しています。私のノードは単なるノード ID です。これが私がやった方法です。専門家がそれについてどう考えているか知りたいだけです。より良い方法はありますか?
これはクラスの実装です (特別なことは何もありません。現在は public/private メソッドは気にしません)
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;
class adjList {
public:
int head;
vector<int> listOfNodes;
void print();
};
void adjList :: print() {
for (int i=0; i<listOfNodes.size(); ++i) {
cout << head << "-->" << listOfNodes.at(i) << endl;
}
}
class graph {
public:
vector<adjList> list;
void print();
};
void graph :: print() {
for (int i=0; i<list.size(); ++i) {
list.at(i).print();
cout << endl;
}
}
メイン関数は、入力ファイルを 1 行ずつ解析します。各行は次のように解釈されます。
<source_node> <node1_connected_to_source_node> <node2_connected_to_source_node <node3_connected_to_source_node> <...>
主なものは次のとおりです。
int main()
{
fstream file("graph.txt", ios::in);
string line;
graph g;
while (getline(file, line)) {
int source;
stringstream str(line);
str >> source;
int node2;
adjList l;
l.head = source;
while (str >> node2) {
l.listOfNodes.push_back(node2);
}
g.list.push_back(l);
}
file.close();
g.print();
getchar();
return 0;
}
main() から変数を直接変更するのではなく、adjList クラス内に addEdge() 関数を追加する必要があることはわかっていますが、今のところ、最適な構造について疑問に思っています。
編集: 私のアプローチには1つの欠点があります。多数のノードを持つ複雑なグラフの場合、ノードは確かに構造体/クラスになり、その場合、オブジェクト全体を格納して値を複製します。その場合、ポインタを使用する必要があると思います。たとえば、無向グラフの場合、ノード オブジェクトのコピーを adjList に格納します (ノード 1 と 2 の間の接続は、1 の隣接リストが 2 になることを意味し、その逆も同様です)。オブジェクト全体ではなく、adjList にノード オブジェクトのポインターを格納することで、これを回避できます。このアプローチの恩恵を受ける dfs 実装を確認してください。そこでは、各ノードが 1 回だけアクセスされるようにする必要があります。同じノードの複数のコピーがあると、私の人生はより困難になります。番号?
この場合、クラス定義は次のように変更されます。
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <map>
using namespace std;
class node {
public:
node() {}
node(int id, bool _dirty): node_id(id), dirty(_dirty) {}
int node_id;
bool dirty;
};
class adjList {
public:
node *head;
vector<node*> listOfNodes;
void print();
~adjList() { delete head;}
};
void adjList :: print() {
for (int i=0; i<listOfNodes.size(); ++i) {
cout << head->node_id << "-->" << listOfNodes.at(i)->node_id << endl;
}
}
class graph {
public:
vector<adjList> list;
void print();
void dfs(node *startNode);
};
void graph::dfs(node *startNode) {
startNode->dirty = true;
for(int i=0; i<list.size(); ++i) {
node *stNode = list.at(i).head;
if (stNode->node_id != startNode->node_id) { continue;}
for (int j=0; j<list.at(i).listOfNodes.size(); ++j) {
if (!list.at(i).listOfNodes.at(j)->dirty) {
dfs(list.at(i).listOfNodes.at(j));
}
}
}
cout << "Node: "<<startNode->node_id << endl;
}
void graph :: print() {
for (int i=0; i<list.size(); ++i) {
list.at(i).print();
cout << endl;
}
}
そして、これが main() 関数を実装した方法です。オブジェクトの重複を避けるために map<> を使用しています。以前に定義されていない場合にのみ、新しいオブジェクトを作成します。ID によるオブジェクトの存在の確認。
int main()
{
fstream file("graph.txt", ios::in);
string line;
graph g;
node *startNode;
map<int, node*> nodeMap;
while (getline(file, line)) {
int source;
stringstream str(line);
str >> source;
int node2;
node *sourceNode;
// Create new node only if a node does not already exist
if (nodeMap.find(source) == nodeMap.end()) {
sourceNode = new node(source, false);
nodeMap[source] = sourceNode;
} else {
sourceNode = nodeMap[source];
}
adjList l;
l.head = sourceNode;
nodeMap[source] = sourceNode;
while (str >> node2) {
// Create new node only if a node does not already exist
node *secNode;
if (nodeMap.find(node2) == nodeMap.end()) {
secNode = new node(node2, false);
nodeMap[node2] = secNode;
} else {
secNode = nodeMap[node2];
}
l.listOfNodes.push_back(secNode);
}
g.list.push_back(l);
startNode = sourceNode;
}
file.close();
g.print();
g.dfs(startNode);
getchar();
return 0;
}
2番目の編集ノードクラスに隣接リストを配置するというUlrich Eckhardtの提案の 後、グラフを保存し、dfs()、dijkstra()のような操作を実行するためのより良いデータ構造であると私が思うものは次のとおりです。隣接リストはノード クラスにマージされることに注意してください。
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <map>
using namespace std;
class node {
public:
node() {
}
node(int id, bool _dirty): node_id(id), dirty(_dirty) {
//cout << "In overloaded const\n";
}
int node_id;
bool dirty;
vector<node*> listOfNodes;
};
class graph {
public:
vector<node*> myGraph;
void dfs(node* startNode);
};
void graph::dfs(node* startNode) {
startNode->dirty = true;
for (int j=0; j<startNode->listOfNodes.size(); ++j) {
if (!startNode->listOfNodes.at(j)->dirty) {
dfs(startNode->listOfNodes.at(j));
}
}
cout << "Node: "<<startNode->node_id << endl;
}
これよりもうまくできるでしょうか?