1. 程式人生 > >NOIP複賽複習(十三)圖論演算法鞏固與提高

NOIP複賽複習(十三)圖論演算法鞏固與提高

一、圖的儲存

 

1、鄰接矩陣

 

假設有n個節點,建立一個n×n的矩陣,第i號節點能到達第j號節點就將[i][j]標記為1(有權值標記為權值), 

樣例如下圖:

 

/*無向圖,無權值*/

int a[MAXN][MAXN];//鄰接矩陣

int x,y;//兩座城市

for(int i=1;i<=n;i++)

{

    for(intj=1;j<=n;j++)

    {

       scanf("%d%d",&x,&y);//

能到達,互相標記為1

        a[x][y]=1;

        a[y][x]=1;

    }

}

/*無向圖,有權值*/

int a[MAXN][MAXN];//鄰接矩陣

int x,y,w;//兩座城市,路徑長度

for(int i=1;i<=n;i++)

{

    for(intj=1;j<=n;j++)

    {

       scanf("%d%d%d",&x,&y,&w);//能到達,互相標記為權值w

        a[x][y]=w;

        a[y][x]=w;

    }

}

/*有向圖,無權值*/

int a[MAXN][MAXN];//鄰接矩陣

int x,y;//兩座城市

for(int i=1;i<=n;i++)

{

    for(intj=1;j<=n;j++)

    {

       scanf("%d%d",&x,&y);//能到達,僅僅是xy標記為1

        a[x][y]=1;

    }

}

/*有向圖,有權值*/

int a[MAXN][MAXN];//鄰接矩陣

int x,y,w;//兩座城市,路徑長度

for(int i=1;i<=n;i++)

{

    for(intj=1;j<=n;j++)

    {

       scanf("%d%d%d",&x,&y,&w);//能到達,僅僅是xy標記為權值w

        a[x][y]=w;

    }

}

 

鄰接矩陣很方便,但是在n過大或者為稀疏圖時,就會很損耗時空,不建議使用!

 

2.鄰接表

 

鄰接表是一個二維容器,第一維描述某個點,第二維描述這個點所對應的邊集們。 

鄰接表由表頭point,鏈點構成,如下圖是一個簡單無向圖構成的鄰接表:

 

我們可以用指標來建立連結串列,當然,這是很複雜也很麻煩的事情,下面來介紹一種用陣列模擬連結串列的方法:

 

//有向圖鄰接表儲存

const int N=1005;

const int M=10050;

int point[N]={0};//i節點所對應連結串列起始位置(表頭)

int to[M]={0};

int next[M]={0};//i節點下一個所指的節點

int cc=0;//計數器(表示第幾條邊)

void AddEdge(int x,int y)//節點xy

{

    cc++;

    to[cc]=y;

    next[cc]=point[x];

    point[x]=cc;

}

void find(int x)

{

    int now=point[x];

    while(now)

    {

       printf("%d\n",to[now]);

        now=next[now];

    }

}

int main()

{

   

}

如果要加強記憶的話可以用我所給的例子模擬一下point[],to[],next[],然後再呼叫函式find(x)來輸出x這個節點能到的點,大概就能YY到陣列是怎麼儲存鄰接表的了。 

還是不理解的話,推一個blog,這裡面說的和我這裡給出的思路很相似:http://developer.51cto.com/art/201404/435072.htm

 

二、樹的遍歷

 

1.BFS

運用佇列,一開始佇列中有一個點,將一個點出隊,將它的子結點全都入隊。 

演算法會在遍歷完一棵樹中每一層的每個結點之後,才會轉到下一層繼續,在這一基礎上,佇列將會對演算法起到很大的幫助:


//廣度優先搜尋

void BreadthFirstSearch(BitNode *root)

{

    queue<BitNode*>nodeQueue;

   nodeQueue.push(root);//將根節點壓入佇列

    while(!nodeQueue.empty())//佇列不為空,繼續壓入佇列

    {

        BitNode *node =nodeQueue.front();

        nodeQueue.pop();//彈出根節點

        if(node->left)//左兒子不為空

        {

           nodeQueue.push(node->left);//壓入佇列

        }

        if(node->right)//右兒子不為空

        {

           nodeQueue.push(node->right);//壓入佇列

        }

    }

}


2.DFS

運用棧,遞迴到一個點時,依次遞迴它的子結點。 

還可以利用堆疊的先進後出的特點,現將右子樹壓棧,再將左子樹壓棧,這樣左子樹就位於棧頂,可以保證結點的左子樹先與右子樹被遍歷:

 

//深度優先搜尋

//利用棧,現將右子樹壓棧再將左子樹壓棧

void DepthFirstSearch(BitNode *root)

{

    stack<BitNode*>nodeStack;

   nodeStack.push(root);//將根節點壓棧

    while(!nodeStack.empty())//棧不為空,繼續壓棧

    {

        BitNode *node =nodeStack.top();//引用棧頂

        cout <<node->data << ' ';

        nodeStack.pop();//彈出根節點

        if(node->right)//優先遍歷右子樹

        {

           nodeStack.push(node->right);

        }

        if (node->left)

        {

           nodeStack.push(node->left);

        }

    }

}


三、無根樹變成有根樹

 

選擇一個點作為根結點,開始遍歷。 

遍歷到一個點時,列舉每一條連線它和另一個點的邊。若另一個點不是