什么是对图表进行排序的有效方法?

时间:2012-11-19 05:52:00

标签: c++ sorting graph

例如,假设有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 left neight是n0,右边邻居是n2。我用index来表示邻居。如果n1位于索引1,则其左邻居位于索引0,右邻居位于索引2.如果我对数组进行排序,那么我也需要更新邻居索引。我不想真正实现我自己的排序算法,有关如何继续的任何建议吗?

4 个答案:

答案 0 :(得分:3)

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

Graph.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

Graph.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;
}

答案 1 :(得分:3)

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

没有什么特别特别的,您需要进行所需的排序。以下是我使用的基本步骤。

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

答案 2 :(得分:0)

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

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

我个人最喜欢的是BogoSort加上平行宇宙理论。理论上说,如果你将机器挂钩到可以破坏宇宙的程序,那么如果列表在一次迭代后没有排序,它将破坏宇宙。这样,除了列表排序的所有并行Universe都将被销毁,并且你有一个复杂度为O(1)的排序算法。

最好的......

答案 3 :(得分:0)

创建一个这样的结构:

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<SortHelper<blah>>,对其进行排序,然后您现在有一个vector指令,说明排序后所有内容的最终结果。

应用这些说明(有几种方法)。一种简单的方法是将container指针重用为bool。走完排序的vector助手。将第一个条目移动到它应该移动的位置,将您找到的位置移动到它应该去的位置,然后重复,直到循环或整个数组被排序。在你去的时候,清除helper结构中的container指针,并检查它们以确保你不移动已经移动的条目(例如,这可以让你检测循环)。

一旦发生循环,继续vector寻找下一个尚未正确的位置条目(带有非空container指针)。