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);//
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);//能到達,僅僅是x到y標記為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);//能到達,僅僅是x到y標記為權值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)//節點x到y
{
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);
}
}
}
三、無根樹變成有根樹
選擇一個點作為根結點,開始遍歷。
遍歷到一個點時,列舉每一條連線它和另一個點的邊。若另一個點不是