【问题标题】:What is an efficient way to sort a graph?什么是对图进行排序的有效方法?
【发布时间】:2012-11-19 05:52:00
【问题描述】:

例如假设有 3 个节点 A、B、C 和 A 链接到 B 和 C,B 链接到 A 和 C,C 链接到 B 和 A。在视觉形式上是这样的

C <- A -> B //A links to B & C
A <- B -> C //B links to A & C
B <- C -> A //C links to B & A

假设 A,B,C 保存在一个数组中,如下所示 [A,B,C],索引从 0 开始。如何根据保存的值有效地对数组 [A,B,C] 进行排序每个节点。

例如,如果 A 持有 4,B 持有 -2,C 持有 -1,那么 sortGraph([A,B,C]) 应该返回 [B,C,A]。希望它清楚。如果我能以某种方式利用 std::sort 有可能吗?

编辑:不是基本的排序算法。让我再澄清一点。假设我有一个节点列表 [n0,n1...nm]。每个 ni 都有一个左右邻居索引。例如,n1 左邻为 n0,其右邻为 n2。我使用索引来表示邻居。如果 n1 在索引 1 处,那么它的左邻居在索引 0 处,而它的右邻居在索引 2 处。如果我对数组进行排序,那么我还需要更新邻居索引。我不想真正实现自己的排序算法,有什么建议可以继续吗?

【问题讨论】:

  • 你问的不只是基本的排序吗?即与图表无关..
  • 这是作业吗? What have you tried?
  • 刚刚重新编辑以解释更多内容
  • 你是说ni 连接到n(i-1)n(i+1)?那有点乱我不认为你可以排序。更改您的节点以具有指向左右的指针,您的原始数组是指向这些节点的指针列表,然后它变得微不足道
  • 另一方面,如果您的意思是每个节点都跟踪左索引和右索引,它仍然可以通过使用指向节点的指针而不是索引来修复

标签: c++ sorting graph


【解决方案1】:

如果我正确理解编辑的问题,您的图表是一个循环链表:每个节点指向前一个和下一个节点,“最后一个”节点指向“第一个”节点作为它的下一个节点。

没有什么特别的,你需要做你想要的那种。以下是我将使用的基本步骤。

  1. 将所有节点放入一个数组中。
  2. 使用任何排序算法对数组进行排序(例如qsort)。
  3. 遍历结果并重置每个节点的 prev/next 指针,同时考虑到第一个和最后一个节点的特殊情况。

【讨论】:

    【解决方案2】:

    这是一个 C++ 实现,希望有用(它包括几个算法,如 dijkstra、kruskal,用于排序它使用深度优先搜索等...)

    图形.h

    #ifndef __GRAPH_H
    #define __GRAPH_H
    
    #include <vector>
    #include <stack>
    #include <set>
    
    typedef struct __edge_t
    {
        int v0, v1, w;
    
        __edge_t():v0(-1),v1(-1),w(-1){}
        __edge_t(int from, int to, int weight):v0(from),v1(to),w(weight){}
    } edge_t;
    
    class Graph
    {
    public:
        Graph(void); // construct a graph with no vertex (and thus no edge)
        Graph(int n); // construct a graph with n-vertex, but no edge
        Graph(const Graph &graph); // deep copy of a graph, avoid if not necessary
    public:
        // @destructor
        virtual ~Graph(void);
    public:
        inline int getVertexCount(void) const { return this->numV; }
        inline int getEdgeCount(void)   const { return this->numE; }
    public:
        // add an edge
        // @param: from [in] - starting point of the edge
        // @param: to   [in] - finishing point of the edge
        // @param: weight[in] - edge weight, only allow positive values
        void addEdge(int from, int to, int weight=1);
        // get all edges
        // @param: edgeList[out] - an array with sufficient size to store the edges
        void getAllEdges(edge_t edgeList[]);
    public:
        // topological sort
        // @param: vertexList[out] - vertex order
        void sort(int vertexList[]);
        // dijkstra's shortest path algorithm
        // @param: v[in] - starting vertex
        // @param: path[out] - an array of <distance, prev> pair for each vertex
        void dijkstra(int v, std::pair<int, int> path[]);
        // kruskal's minimum spanning tree algorithm
        // @param: graph[out] - the minimum spanning tree result
        void kruskal(Graph &graph);
        // floyd-warshall shortest distance algorithm
        // @param: path[out] - a matrix of <distance, next> pair in C-style
        void floydWarshall(std::pair<int, int> path[]);
    private:
        // resursive depth first search
        void sort(int v, std::pair<int, int> timestamp[], std::stack<int> &order);
        // find which set the vertex is in, used in kruskal
        std::set<int>* findSet(int v, std::set<int> vertexSet[], int n);
        // union two sets, used in kruskal
        void setUnion(std::set<int>* s0, std::set<int>* s1);
        // initialize this graph
        void init(int n);
        // initialize this graph by copying another
        void init(const Graph &graph);
    private:
        int numV, numE; // number of vertices and edges
        std::vector< std::pair<int, int> >* adjList; // adjacency list
    };
    
    #endif
    

    图形.cpp

    #include "Graph.h"
    #include <algorithm>
    #include <map>
    
    Graph::Graph()
    :numV(0), numE(0), adjList(0)
    {
    }
    
    Graph::Graph(int n)
    :numV(0), numE(0), adjList(0)
    {
        this->init(n);
    }
    
    Graph::Graph(const Graph &graph)
    :numV(0), numE(0), adjList(0)
    {
        this->init(graph);
    }
    
    Graph::~Graph()
    {
        delete[] this->adjList;
    }
    
    void Graph::init(int n)
    {
        if(this->adjList){
            delete[] this->adjList;
        }
        this->numV = n;
        this->numE = 0;
        this->adjList = new std::vector< std::pair<int, int> >[n];
    }
    
    void Graph::init(const Graph &graph)
    {
        this->init(graph.numV);    
        for(int i = 0; i < numV; i++){
            this->adjList[i] = graph.adjList[i];
        }
    }
    
    void Graph::addEdge(int from, int to, int weight)
    {
        if(weight > 0){
            this->adjList[from].push_back( std::make_pair(to, weight) );
            this->numE++;
        }
    }
    
    void Graph::getAllEdges(edge_t edgeList[])
    {
        int k = 0;
        for(int i = 0; i < numV; i++){
            for(int j = 0; j < this->adjList[i].size(); j++){
                // add this edge to edgeList
                edgeList[k++] = edge_t(i, this->adjList[i][j].first, this->adjList[i][j].second);
            }
        }
    }
    
    void Graph::sort(int vertexList[])
    {
        std::pair<int, int>* timestamp = new std::pair<int, int>[this->numV];
        std::stack<int> order;
    
        for(int i = 0; i < this->numV; i++){
            timestamp[i].first = -1;
            timestamp[i].second = -1;
        }
    
        for(int v = 0; v < this->numV; v++){
            if(timestamp[v].first < 0){
                this->sort(v, timestamp, order);
            }
        }
    
        int i = 0;
        while(!order.empty()){
            vertexList[i++] = order.top();
            order.pop();
        }
        delete[] timestamp;
        return;
    }
    
    void Graph::sort(int v, std::pair<int, int> timestamp[], std::stack<int> &order)
    {
        // discover vertex v
        timestamp[v].first = 1;
    
        for(int i = 0; i < this->adjList[v].size(); i++){
            int next = this->adjList[v][i].first;
            if(timestamp[next].first < 0){
                this->sort(next, timestamp, order);
            }
        }
        // finish vertex v
        timestamp[v].second = 1;
        order.push(v);
        return;
    }
    
    void Graph::dijkstra(int v, std::pair<int, int> path[])
    {
        int* q = new int[numV];
        int numQ = numV;
    
        for(int i = 0; i < this->numV; i++){
            path[i].first = -1; // infinity distance
            path[i].second = -1; // no path exists
            q[i] = i;
        }
    
        // instant reachable to itself
        path[v].first = 0;
        path[v].second = -1;
    
        while(numQ > 0){
            int u = -1; // such node not exists
            for(int i = 0; i < numV; i++){
                if(q[i] >= 0 
                && path[i].first >= 0 
                && (u < 0 || path[i].first < path[u].first)){ // 
                    u = i;
                }
            }
    
    
            if(u == -1){
                // all remaining nodes are unreachible
                break;
            }
            // remove u from Q
            q[u] = -1;
            numQ--;
    
            for(int i = 0; i < this->adjList[u].size(); i++){
                std::pair<int, int>& edge = this->adjList[u][i];
                int alt = path[u].first + edge.second;
    
                if(path[edge.first].first < 0 || alt < path[ edge.first ].first){
                    path[ edge.first ].first = alt;
                    path[ edge.first ].second = u;
                }
            }
        }
    
        delete[] q;
        return;
    }
    
    // compare two edges by their weight
    bool edgeCmp(edge_t e0, edge_t e1)
    {
        return e0.w < e1.w;
    }
    
    std::set<int>* Graph::findSet(int v, std::set<int> vertexSet[], int n)
    {
        for(int i = 0; i < n; i++){
            if(vertexSet[i].find(v) != vertexSet[i].end()){
                return vertexSet+i;
            }
        }
        return 0;
    }
    
    void Graph::setUnion(std::set<int>* s0, std::set<int>* s1)
    {
        if(s1->size() > s0->size()){
            std::set<int>* temp = s0;
            s0 = s1;
            s1 = temp;
        }
    
        for(std::set<int>::iterator i = s1->begin(); i != s1->end(); i++){
            s0->insert(*i);
        }
        s1->clear();
        return;
    }
    
    void Graph::kruskal(Graph &graph)
    {
        std::vector<edge_t> edgeList;
        edgeList.reserve(numE);
        for(int i = 0; i < numV; i++){
            for(int j = 0; j < this->adjList[i].size(); j++){
                // add this edge to edgeList
                edgeList.push_back( edge_t(i, this->adjList[i][j].first, this->adjList[i][j].second) );
            }
        }
    
        // sort the list in ascending order
        std::sort(edgeList.begin(), edgeList.end(), edgeCmp);
    
        graph.init(numV);   
        // create disjoint set of the spanning tree constructed so far
        std::set<int>* disjoint = new std::set<int>[this->numV];
        for(int i = 0; i < numV; i++){
            disjoint[i].insert(i);
        }
    
        for(int e = 0; e < edgeList.size(); e++){
            // consider edgeList[e]
            std::set<int>* s0 = this->findSet(edgeList[e].v0, disjoint, numV);
            std::set<int>* s1 = this->findSet(edgeList[e].v1, disjoint, numV);
            if(s0 == s1){
                // adding this edge will make a cycle
                continue;
            }
    
            // add this edge to MST
            graph.addEdge(edgeList[e].v0, edgeList[e].v1, edgeList[e].w);
            // union s0 & s1
            this->setUnion(s0, s1);
        }
        delete[] disjoint;
        return;
    }
    
    #define IDX(i,j)    ((i)*numV+(j))
    
    void Graph::floydWarshall(std::pair<int, int> path[])
    {
        // initialize
        for(int i = 0; i < numV; i++){
            for(int j = 0; j < numV; j++){
                path[IDX(i,j)].first = -1;
                path[IDX(i,j)].second = -1;
            }
        }
        for(int i = 0; i < numV; i++){
            for(int j = 0; j < this->adjList[i].size(); j++){
                path[IDX(i,this->adjList[i][j].first)].first
                    = this->adjList[i][j].second;
                path[IDX(i,this->adjList[i][j].first)].second
                    = this->adjList[i][j].first;
            }
        }
    
        // dynamic programming
        for(int k = 0; k < numV; k++){
            for(int i = 0; i < numV; i++){
                for(int j = 0; j < numV; j++){
                    if(path[IDX(i,k)].first == -1
                    || path[IDX(k,j)].first == -1){
                        // no path exist from i-to-k or from k-to-j
                        continue;
                    }
    
                    if(path[IDX(i,j)].first == -1
                    || path[IDX(i,j)].first > path[IDX(i,k)].first + path[IDX(k,j)].first){
                        // there is a shorter path from i-to-k, and from k-to-j
                        path[IDX(i,j)].first = path[IDX(i,k)].first + path[IDX(k,j)].first;
                        path[IDX(i,j)].second = k;
                    }
                }
            }
        }
        return;
    }
    

    【讨论】:

      【解决方案3】:

      如果你正在寻找排序算法,你应该问谷歌:

      http://en.wikipedia.org/wiki/Sorting_algorithm

      我个人最喜欢的是结合平行宇宙理论的 BogoSort。该理论是,如果您将一台机器连接到可以破坏宇宙的程序,那么如果列表在一次迭代后没有排序,它将破坏宇宙。这样一来,除了列表排序的平行宇宙之外的所有平行宇宙都将被破坏,并且您有一个复杂度为 O(1) 的排序算法。

      最好的....

      【讨论】:

        【解决方案4】:

        创建一个这样的结构:

        template<typename Container, typename Comparison = std::less<typename Container::value_type>>
        struct SortHelper
        {
            Container const* container;
            size_t org_index;
            SortHelper( Container const* c, size_t index ):container(c), org_index(index) {}
            bool operator<( SortHelper other ) const
            {
              return Comparison()( (*c)[org_index], (*other.c)[other.org_index] );
            }
        };
        

        这让您可以随心所欲地使用任何东西。

        现在,创建一个std::vector&lt;SortHelper&lt;blah&gt;&gt;,对其进行排序,您现在就有了一个vector 的说明,说明排序后所有内容的最终去向。

        应用这些说明(有几种方法)。一种简单的方法是将container 指针作为布尔值重用。遍历排序的vector 助手。将第一个条目移动到它应该去的地方,将你找到的东西移动到它应该去的地方,然后重复直到你循环或整个数组被排序。随着您的进行,清除辅助结构中的 container 指针,并检查它们以确保您不会移动已移动的条目(例如,这可以让您检测循环)。

        一旦发生循环,继续沿 vector 向下寻找下一个 as-yet-not-in-right-place 条目(带有非空 container 指针)。

        【讨论】:

          猜你喜欢
          • 2010-11-07
          • 1970-01-01
          • 2012-08-05
          • 2020-12-30
          • 2011-04-22
          • 2010-11-05
          • 2016-10-19
          • 2016-03-20
          相关资源
          最近更新 更多