1. 程式人生 > >遍歷尋找給定兩點之間的所有路徑

遍歷尋找給定兩點之間的所有路徑

直接入題,參加2017年華為競賽的時候,需要輸出路徑,當時設了源點和匯點,是單源單匯的尋路徑,那麼問題就歸為尋找給定兩點之間的所有路徑問題

我們的老師一直教我們這些菜鳥們說,寫程式一定要先在紙上寫好考慮清楚,這次我覺得這個應該挺簡單的,就沒寫,沒想到出現了錯誤,難調,搞了好久,死迴圈。然後自己乖乖的關掉vs,乖乖寫思路。
首先,確認目的,也就是輸入,也就是我們題目的問題:尋找給定兩點之間的所有路徑,上個例子吧,好說也好順思路:
一張盜別人的圖
假如我們需要找0->11之間所有路徑,那麼我們希望得到的輸出結果應該就是:
路徑1:0–>1–>2–>5–>7–>11
路徑2: 0–>1–>2–>5–>8–>11
路徑3: 0–>1–>2–>6–>9–>11
路徑4:0–>1–>2–>6–>10–>11
路徑5:0–>1–>3–>11
路徑6: 0–>1–>4–>11
到此,程式輸入輸出確定:

  • 輸入:帶有節點和邊的資訊的鄰接矩陣
  • 輸出:就是上面的提到的路徑

建立上圖的鄰接矩陣A,節點i->j之間有邊,則A(i,j)==1
上圖的鄰接矩陣就是:

這裡寫圖片描述

接下來就是怎麼輸出來路徑,DFS,BFS都行,先想想如果自己去找路徑,怎麼搜?也好想,從源點“0”出發,由於編號是按順序的,而且終點是編號最大的那個點,我就偷個懶,特殊情況特殊對待吧,還是畫圖最直觀。
還是來看鄰接矩陣:

這裡寫圖片描述

首先將起點 0 入棧,設定入棧狀態為1,從例圖可以看出,與0相連的,最先開始搜到 1 ,即第0行第1列紅色框,將節點1入棧,入棧狀態設定為1,繼續,從第1行開始搜,搜到第一個非零點,即第1行第2列的紅色框,將節點2入棧,如此往復,知道找到終點 11 ,就得到了路徑:0–>1–>2–>5–>7–>11。
這裡寫圖片描述


接下來要搜尋下一條路徑,將節點 11 出棧,入棧狀態設定為 0 ,在考慮是否要將節點7出棧,我們從例圖可以看到,第7行我們一行都已經搜尋完畢了,都搜尋到終點 11 了,那麼節點7就必須出棧,因為如果不出棧,從節點 7 搜尋就會再一次搜尋到節點 11,那麼就陷入死迴圈了,所以必須將 7 出棧,而且應該從節點 7 之後的搜尋,因為節點 7 之前要麼是已經搜尋過得,要麼就是第一個搜尋到的,所以需要記錄終點之前的節點位置,那麼接下來,可以看到節點 8 非零,那麼從第 8 行開始搜尋,檢查搜到的節點的入棧狀態,又搜尋到終點 11 ,那麼就搜到路徑:0–>1–>2–>5–>8–>11,以便從其之後繼續搜尋路徑,依次輸出各個路徑,直至棧內為空為止。
那麼需要的變數應該是:鄰接矩陣,入棧狀態變數,儲存path組的結構,以及儲存單個path的vector, 儲存尋找路徑的棧,以及一個記錄位置變數。

程式碼如下:

#include<iostream>
#include<vector>
#include<stack>
#define MAX_EDGE_NUM  500
#define MAX_LINE_LEN 200
using namespace std;
//讀取檔案的函式
int read_file(char ** const buff, const unsigned int spec, const char * const filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp == NULL)
    {
        printf("Fail to open file %s, %s.\n", filename, strerror(errno));
        return 0;
    }
    printf("Open file %s OK.\n", filename);

    char line[MAX_LINE_LEN + 2];
    unsigned int cnt = 0;
    while ((cnt < spec) && !feof(fp))
    {
        line[0] = 0;
        if (fgets(line, MAX_LINE_LEN + 2, fp) == NULL)  continue;
        if (line[0] == 0)   continue;
        buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);
        strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);
        buff[cnt][MAX_LINE_LEN + 1] = 0;
        cnt++;
    }
    fclose(fp);
    printf("There are %d lines in file %s.\n", cnt, filename);

    return cnt;
}
//將每行的資料轉換為int的函式
void  char2int(char* char_data, int *int_data, int int_length)
{
    char c[10];
    int num = 0;
    int count = 0;
    for (int i = 0; i < 10;)
    {
        count = 0;
        while (char_data[i] != ' ' && char_data[i] != '\n' && char_data[i] != '\r' && count <10)
        {
            c[count] = char_data[i];
            i++;
            count++;
        }
        int_data[num] = atoi(c);
        memset(c, 0, 10);
        ++num;
        if (num >= int_length)
        {
            return;
        }
        i++;
    }
}
int main(int argc, char *argv[])
{

    char *topo[MAX_EDGE_NUM];
    int line_num;
    //讀取檔案,需要在屬性--配置屬性--除錯--命令引數,以及工作目錄設定所讀取的檔案node.txt
    char *topo_file = argv[1];
    line_num = read_file(topo, MAX_EDGE_NUM, topo_file);


    printf("line num is :%d \n", line_num);
    if (line_num == 0)
    {
        printf("Please input valid topo file.\n");
        return -1;
    }
    //鄰接矩陣
    int node_num = 11;
    short **adjcent_Matrix = new short*[node_num + 1]();//初始化鄰接矩陣全為0
    for (int i = 0; i < node_num; i++)
    {
        adjcent_Matrix[i] = new short[node_num + 1]();
    }
    //按照例圖填充鄰接矩陣
    int start_node,end_node;
    int data2[2];
    for (int i = 0; i < line_num; i++)
    {
        char2int(topo[i], data2, 2);
        start_node = data2[0];
        end_node = data2[1];
        adjcent_Matrix[start_node][end_node] = 1;
    }
    bool* is_in_stack = new bool[node_num+1]();//入棧狀態變數
    stack<int>node_stack;
    int c_position = 0;
    vector<vector<int>>paths;//儲存所有路徑
    vector<int>path;//儲存單條路徑

    //起點入棧
    node_stack.push(0);
    is_in_stack[0] = 1;//設定起點已入棧,1表示在棧中,0 表示不在
    int top_element;//記錄棧頂元素
    int tmp;
    while (!node_stack.empty())
    {
        top_element = node_stack.top();//檢視棧頂元素,判斷是否已經到達終點
        if (top_element == node_num)//若到達終點,輸出路徑,彈出棧中兩個點,設定出棧狀態
        {
            while (!node_stack.empty())
            {
                tmp = node_stack.top();
                node_stack.pop();
                path.push_back(tmp);
            }
            paths.push_back(path);//將路徑加入路徑組
            for (vector<int>::reverse_iterator rit = path.rbegin(); rit != path.rend(); rit++)
            {
                node_stack.push(*rit);
            }
            path.clear();//清除單條路徑

            node_stack.pop();
            is_in_stack[top_element] = 0;
            c_position = node_stack.top();//記錄位置,以便從該位置之後進行搜尋
            top_element = node_stack.top();
            node_stack.pop();
            is_in_stack[top_element] = 0; //cout << vis[top_element];
        }
        else
        {
            int i = 0;
            for (i = c_position + 1; i <node_num + 2; i++)
            {

                if (is_in_stack[i] == 0 && adjcent_Matrix[top_element][i] != 0)//未入棧,而且節點之間有邊相連
                {

                    is_in_stack[i] = 1;//stack In
                    node_stack.push(i);//入棧
                    c_position = 0;//位置置零,是因為從記錄的位置開始搜尋完以後,在新的行上搜索,自然從零開始,以免漏掉節點
                    break;
                }
            }
            if (i == node_num + 2)
            {
                top_element = node_stack.top();
                is_in_stack[top_element] = 0;
                c_position = node_stack.top();
                node_stack.pop();
            }
        }
    }
    //========================  輸出 ==========================
    //逆向
    for (int i = 0; i <paths.size(); i++)
    {
        cout << "路徑" << i << ": ";
        for (int j = paths[i].size()-1; j >=0; j--)
        {
            if (j == 0)
            {
                cout << paths[i][j];
            }
            else
            {
                cout << paths[i][j] << "->";
            }

        }
        cout << endl;
    }
    //========================  輸出 ==========================

    //刪除記憶體
    for (int i = 0; i < node_num ; i++)
    {
        delete[] adjcent_Matrix[i];
    }
    delete[] adjcent_Matrix;
}

他強任他強!