【发布时间】:2021-04-03 15:32:52
【问题描述】:
即使在 Steven Skiena 第二版的“算法设计手册”一书中的 Skiena 的 dfs 实现中包含勘误表更改后,我仍会陷入无限循环,我想知道如何修复它。 原因似乎很明显,适用于任何让我认为可能是我做错但无法弄清楚的无向图。
取任何无向图(如果你有书的话,也可以是 pg171 上的那个),假设有一条边 (1,6)。在邻接列表中,alist[6] 将有一个节点为 1,而 alist[1] 将有一个节点为 6。在顶点 = 1 时启动 DFS(调用 dfs(1)),它首先发现 6 并设置 parent[6]= 1.然后递归调用 dfs(6) 想要发现 1 但已经发现了 1。这会导致 while 循环中的第一个 if 条件变为 false
if (!discovered[y]) )
因此不设置 parent[1]。
1 尚未处理,并且 parent[6] 为 1 (i-e y),如前一次迭代中设置的那样,因此 else if 循环中的条件也为 false。
else if (((!processed[y]) && (parents[v] != y)) || (directed))
由于else条件为假,我们没有将指针p重置到链表中的下一个节点,所以它进入了一个无限循环
while (p != nullptr)
所以基本上它在处理第一条边 (1,6) 和 (6,1) 时卡住了,这应该发生在任何无向图上。这个无限循环的修复方法是什么?还是我在这里做错了与skiena实施不同的事情?
这是重现无限循环的最小可编译和可运行代码,包括 main()
#include <iostream>
#include <queue>
const int MAX_VERTICES = 10000;
struct EdgeNode {
private:
int y{ -1 };
EdgeNode* next{ nullptr };
public:
EdgeNode(int _y, EdgeNode* _next) : y{ _y }, next{ _next }{}
EdgeNode(int _y) : y{ _y }, next{ nullptr}{}
const int getY() const { return y; } ;
const EdgeNode* const getNext() const { return next; };
};
class Graph {
EdgeNode* edges[MAX_VERTICES]{ nullptr };
int degree[MAX_VERTICES]{ 0 };
int totalVertices{ 0 };
int totalEdges{ 0 };
bool directed{ false };
bool processed[MAX_VERTICES]{ false };
bool discovered[MAX_VERTICES]{ false };
bool finished = false;
int parents[MAX_VERTICES];
void initializeSearch() {
for (int i = 0; i < MAX_VERTICES; i++)
{
parents[i] = -1;
processed[i] = false;
discovered[i] = false;
}
finished = false;
}
public:
int Vertices() const {return totalVertices; }
int Edges() const { return totalEdges; }
const EdgeNode* getEdge(int x) const {
if (x > MAX_VERTICES) return nullptr;
return edges[x];
}
bool insertEdge(int x, int y) { return insertEdge(x, y, false); }
bool insertEdge(int x, int y, bool _directed) {
if (x > MAX_VERTICES) { std::cout << std::endl << "Unable to insert edge. Max vertices allowed:" << MAX_VERTICES; return false; }
EdgeNode* temp = new EdgeNode(y, edges[x]);
if (degree[x] == 0) totalVertices++;
edges[x] = temp;
degree[x]++;
totalEdges++;
if (!_directed) {
insertEdge(y, x, true);
}
return true;
}
void process_vertex_late(int vertex) {}
void process_vertex_early(int vertex) {std::cout << std::endl << "Processing Vertex: " << vertex;}
void process_edge_dfs(int x, int y) {
std::cout << std::endl << "\tProcessing Edge(" << x << "," << y << ")";
if (discovered[y] && (parents[x] != y)) {
std::cout << std::endl << "Cycle(" << x << "," << y << ")";
std::cout << std::endl << std::endl;
finished = true;
}
}
void dfs1(int start) {
initializeSearch();
dfs(start, false);
}
void dfs(int v, bool print) {
const EdgeNode* p;
int y;
if (finished)
return;
discovered[v] = true;
process_vertex_early(v);
p = getEdge(v);
while (p != nullptr) {
y = p->getY();
if (!discovered[y]) {
parents[y] = v;
process_edge_dfs(v, y);
dfs(y, false);
}
else if (((!processed[y]) && (parents[v] != y)) || (directed))
{
process_edge_dfs(v, y);
if (finished)
return;
p = p->getNext();
}
}
process_vertex_late(v);
processed[v] = true;
}
};
int main()
{
Graph graph;
graph.insertEdge(1, 2);
graph.insertEdge(1, 5);
graph.insertEdge(1, 6);
graph.insertEdge(2, 5);
graph.insertEdge(2, 3);
graph.insertEdge(3, 4);
graph.insertEdge(4, 5);
graph.dfs1(1);
return 0;
}
【问题讨论】:
-
解决这个无限循环的方法是什么? -- 改变你的计划并重新编码你的程序。如果程序有问题,那么您是否不知道要更改哪些内容才能使其遵循您的计划?
-
如果我知道如何修复 dfs 实现,那我为什么要问?
-
一个解决方法是在 while 循环退出之前添加它,但我不知道它是否仍然是有效的 dfs 实现。 p = p->getNext();
-
那么你为什么不实施你的修复,看看它是否有效?
-
另外,您假设我们知道“Skiena”是什么(或谁),或者某本书第 107 页上的内容。如果您要获取代码并对其进行更改,您有责任 100% 了解该代码的作用、它是如何工作的,以及您所做的任何更改都会产生什么影响。否则会变成cargo cult programming的形式,这不是一个好习惯。
标签: c++ algorithm graph infinite-loop depth-first-search