Stanford - Algorithms: Design and Analysis, Part 1 - Week 4 Assignment: strongly connected component

本次作业要求如下:

Question 1

Download the text file  here. Zipped version  here. (Right click and save link as)

The file contains the edges of a directed graph. Vertices are labeled as positive integers from 1 to 875714. Every row indicates an edge, the vertex label in first column is the tail and the vertex label in second column is the head (recall the graph is directed, and the edges are directed from the first column vertex to the second column vertex). So for example, the  11th  row looks liks : "2 47646". This just means that the vertex with label 2 has an outgoing edge to the vertex with label 47646

Your task is to code up the algorithm from the video lectures for computing strongly connected components (SCCs), and to run this algorithm on the given graph. 

Output Format: You should output the sizes of the 5 largest SCCs in the given graph, in decreasing order of sizes, separated by commas (avoid any spaces). So if your algorithm computes the sizes of the five largest SCCs to be 500, 400, 300, 200 and 100, then your answer should be "500,400,300,200,100". If your algorithm finds less than 5 SCCs, then write 0 for the remaining terms. Thus, if your algorithm computes only 3 SCCs whose sizes are 400, 300, and 100, then your answer should be "400,300,100,0,0".

WARNING: This is the most challenging programming assignment of the course. Because of the size of the graph you may have to manage memory carefully. The best way to do this depends on your programming language and environment, and we strongly suggest that you exchange tips for doing this on the discussion forums.

这次的作业是相当有难度的,是用kosaraju algorithm求解Strongly Connected Components(SCCs):

所谓SCCs,下面一张图可以很好地说明:


说白了,就是directed graph中的两个vertex:v和w,v可以到达w,w也可以到达v,他俩就算SCCs

有一种很巧妙的算法可以解决这个问题,就是kosaraju算法,该算法的正确性很难理解,不过好在这也不是本次作业,或者将来大公司技术面试的重点,所以忽略。。

这里只讨论该算法的描述过程,以及如果用C++实现,这个算法的描述其实并不复杂,但是足够神奇:

下面两张图大致介绍了该算法的伪代码:


需要对Grev进行一次DFS-loop

然后再对G进行一次DFS-loop

DFS-lopp由下图说明:


下面就针对算法的各个模块说明我的C++实现方式:

1,获得Grev:

我用了一种看起来相当二缺的方式做的,比如我采用adjacency list的方式,从txt导入的时候,如果有一个是1->2,则我在这个后面再设定一个新的adjacency list,存在2->1:

导入的代码如下:

/* store the txt file into adjacency list */
void store_file(map<int, adjacency>& adjacency_list, map<int, adjacency>& reverse_list, string filename) {
    ifstream infile;
    infile.open(filename, ios::in);
    int tmp1, tmp2;
    map<int, adjacency>::iterator it;
    while (infile >> tmp1 >> tmp2) {
        /* tmp1->tmp2 */
        if (adjacency_list.find(tmp1) == adjacency_list.end()) {
            vector<int> tmp_vec(1, tmp2);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp1;
            adj_tmp.list = tmp_vec;
            adjacency_list.insert(pair<int, adjacency>(tmp1, adj_tmp));
        }
        else {
            adjacency_list[tmp1].list.push_back(tmp2);
        }
        if (adjacency_list.find(tmp2) == adjacency_list.end()) {
            vector<int> tmp_vec(0);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp2;
            adj_tmp.list = tmp_vec;
            adjacency_list.insert(pair<int, adjacency>(tmp2, adj_tmp));
        }
        if (reverse_list.find(tmp2) == reverse_list.end()) {
            vector<int> tmp_vec(1, tmp1);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp2;
            adj_tmp.list = tmp_vec;
            reverse_list.insert(pair<int, adjacency>(tmp2, adj_tmp));
        }
        else {
            reverse_list[tmp2].list.push_back(tmp1);
        }
        if (reverse_list.find(tmp1) == reverse_list.end()) {
            vector<int> tmp_vec(0);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp1;
            adj_tmp.list = tmp_vec;
            reverse_list.insert(pair<int, adjacency>(tmp1, adj_tmp));
        }
    }
    infile.close();
}
2,获得order

首先对Grev进行dfs-loop:

for (map<int, adjacency>::reverse_iterator rit = reverse_list.rbegin(); rit != reverse_list.rend(); ++rit) {
            if (rit->second.visited == false) {
                int sz = 0;
                dfs(adjacency_list ,reverse_list, rit->second, op, sz);
            }
        }
然后是dfs:

for (int i = 0; i < node.list.size(); ++i) {
            if (reverse_list[node.list[i]].visited == false)
                dfs(adjacency_list, reverse_list, reverse_list[node.list[i]], op, sz);
        }
        stack_list.push(adjacency_list[node.name]);
我是根据完成的时间存进了stack中,这样获得order,方便下一步的使用。

3, Run dfs-loop on G

首先是根据上面的stack中的order进行dfs-loop:

<span style="white-space:pre">	</span>while (!stack_list.empty()) {
            if (adjacency_list[stack_list.top().name].visited == false) {
                int sz = 1;
                dfs(adjacency_list, reverse_list, stack_list.top(), op, sz);
                size_set.insert(sz);
            }
            stack_list.pop();
        }
然后进行dfs:

<span style="white-space:pre">	</span>adjacency_list[node.name].visited = true;
        for (int i = 0; i < node.list.size(); ++i) {
            if (adjacency_list[node.list[i]].visited == false) {
                ++sz;
                dfs(adjacency_list, reverse_list, adjacency_list[node.list[i]], op, sz);
            }
        }
以上就是全部过程,

下面是完整代码,这次的代码写的非常之丑,数据结构的使用也没有深思熟虑,时间太赶了,不过这是我的个人代码,不是团队project,就先将就吧。。。

# include <iostream>
# include <fstream>
# include <map>
# include <vector>
# include <string>
# include <stack>
# include <set>

using namespace std;

struct adjacency {
    int name;
    bool visited;
    vector<int> list;
};

typedef int option;

/* function prototype */
void store_file(map<int, adjacency>&, map<int, adjacency>&, string);
void print_adjacency_list(map<int, adjacency>&);
void dfs_loop(map<int, adjacency>&, map<int, adjacency>&, option);
void dfs(map<int, adjacency>&, map<int, adjacency>&, adjacency&, option, int&);
void print_stack();
void print_set();

stack<adjacency> stack_list;
multiset<int> size_set;

int main(int argc, char** argv) {

    map<int, adjacency> adjacency_list;
    map<int, adjacency> reverse_list;

    store_file(adjacency_list, reverse_list, "SCC.txt");
//    cout << "adjacency_list: " << endl;
//    print_adjacency_list(adjacency_list);
//    cout << "reverse_list: " << endl;
//    print_adjacency_list(reverse_list);

    dfs_loop(adjacency_list, reverse_list, 1);
    dfs_loop(adjacency_list, reverse_list, 2);

//    print_stack();
    print_set();

    return 0;
}

/* store the txt file into adjacency list */
void store_file(map<int, adjacency>& adjacency_list, map<int, adjacency>& reverse_list, string filename) {
    ifstream infile;
    infile.open(filename, ios::in);
    int tmp1, tmp2;
    map<int, adjacency>::iterator it;
    while (infile >> tmp1 >> tmp2) {
        /* tmp1->tmp2 */
        if (adjacency_list.find(tmp1) == adjacency_list.end()) {
            vector<int> tmp_vec(1, tmp2);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp1;
            adj_tmp.list = tmp_vec;
            adjacency_list.insert(pair<int, adjacency>(tmp1, adj_tmp));
        }
        else {
            adjacency_list[tmp1].list.push_back(tmp2);
        }
        if (adjacency_list.find(tmp2) == adjacency_list.end()) {
            vector<int> tmp_vec(0);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp2;
            adj_tmp.list = tmp_vec;
            adjacency_list.insert(pair<int, adjacency>(tmp2, adj_tmp));
        }
        if (reverse_list.find(tmp2) == reverse_list.end()) {
            vector<int> tmp_vec(1, tmp1);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp2;
            adj_tmp.list = tmp_vec;
            reverse_list.insert(pair<int, adjacency>(tmp2, adj_tmp));
        }
        else {
            reverse_list[tmp2].list.push_back(tmp1);
        }
        if (reverse_list.find(tmp1) == reverse_list.end()) {
            vector<int> tmp_vec(0);
            adjacency adj_tmp;
            adj_tmp.visited = false;
            adj_tmp.name = tmp1;
            adj_tmp.list = tmp_vec;
            reverse_list.insert(pair<int, adjacency>(tmp1, adj_tmp));
        }
    }
    infile.close();
}

/* print the adjacency list */
void print_adjacency_list(map<int, adjacency>& adjacency_list) {
    for (map<int, adjacency>::iterator it = adjacency_list.begin(); it != adjacency_list.end(); ++it) {
        cout << it->first << ": ";
        for (int i = 0; i < it->second.list.size(); ++i) {
            cout << it->second.list[i] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

void dfs_loop(map<int, adjacency>& adjacency_list, map<int, adjacency>& reverse_list, option op) {
    if (op == 1) {
        for (map<int, adjacency>::reverse_iterator rit = reverse_list.rbegin(); rit != reverse_list.rend(); ++rit) {
            if (rit->second.visited == false) {
                int sz = 0;
                dfs(adjacency_list ,reverse_list, rit->second, op, sz);
            }
        }
    }
    else if (op == 2) {
        while (!stack_list.empty()) {
            if (adjacency_list[stack_list.top().name].visited == false) {
                int sz = 1;
                dfs(adjacency_list, reverse_list, stack_list.top(), op, sz);
                size_set.insert(sz);
            }
            stack_list.pop();
        }
    }
}

void dfs(map<int, adjacency>& adjacency_list, map<int, adjacency>& reverse_list, adjacency& node, option op, int& sz) {
    node.visited = true;
    if (op == 1) {
        for (int i = 0; i < node.list.size(); ++i) {
            if (reverse_list[node.list[i]].visited == false)
                dfs(adjacency_list, reverse_list, reverse_list[node.list[i]], op, sz);
        }
        stack_list.push(adjacency_list[node.name]);
    }
    else if (op == 2) {
        adjacency_list[node.name].visited = true;
        for (int i = 0; i < node.list.size(); ++i) {
            if (adjacency_list[node.list[i]].visited == false) {
                ++sz;
                dfs(adjacency_list, reverse_list, adjacency_list[node.list[i]], op, sz);
            }
        }
    }
}

/* print the stack */
void print_stack() {
    while (!stack_list.empty()) {
        cout << " " << stack_list.top().name;
        stack_list.pop();
    }
    cout << endl;
}

/* print top 5 set */
void print_set() {
    cout << "top size: " << endl;
    int i = 0;
    for (auto rit = size_set.crbegin(); rit != size_set.crend(); ++rit) {
        ++i;
        cout << *rit << endl;
        if (i == 5)
            break;
    }
    while (i < 5) {
        cout << 0 << endl;
        ++i;
    }
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值