【问题标题】:How to fix the infinite loop in Skiena's dfs (depth first search) implementation c++如何修复 Skiena 的 dfs(深度优先搜索)实现 C++ 中的无限循环
【发布时间】: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


【解决方案1】:

希望你能找到答案。

我有这本书的第二版,我知道你的错误是什么。

你写的

else if (((!processed[y]) && (parents[v] != y)) || (directed))
{
                process_edge_dfs(v, y);
                if (finished) 
                    return;
                p = p->getNext();
}

正确的实现是:

else if (((!processed[y]) && (parents[v] != y)) || (directed))
   process_edge_dfs(v, y);

if (finished) return;
p = p->getNext();
...

在我的版本中,代码在两页上,对齐方式不一样。 这很混乱。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多