圖 學習總結

圖的基本內容

鄰接表和鄰接矩陣的實現方式將在具體算法實現中完成,不做贅述。
鄰接矩陣的遍歷:
1.深度優先遍歷:(遞歸實現)一直走不回頭
(1)訪問指定起始地點。
(2)若當前訪問頂點的鄰接頂點有未被訪問的頂點,就任選一個訪問。如果沒有就回退到最近訪問的頂點,直到與起始頂點相通的所有點被遍歷完。
(3)若途中還有頂點未被訪問,則再選一個點作爲起始頂點。重複步驟2(針對非連通圖)。
2.廣度優先遍歷:(隊列實現)走完一個點的所有臨界點再往下走
(1)訪問指定起始點。
(2)訪問當前頂點的鄰接頂點有未被訪問的頂點,並將之放入隊列中。
(3)刪除隊列的隊首節點。訪問當前隊列的隊首,重複步驟2。直到隊列爲空。
(4)若若途中還有頂點未被訪問,則再選一個點作爲起始頂點。重複步驟2。(針對非連通圖)。

#include<iostream>
#include<queue>
using namespace std;
const int MaxSize = 100;
int MinEdge(int a[],int n)
{
    int min = 100;
    int index = 1;
    for(int i = 1;i<=n;i++)
    {
        if(a[i]<=min&&a[i]!=0)
        {
            min = a[i];
            index = i;
        }
    }
    return index;
}
template <typename DataType>
class MGraph
{
    public:
    MGraph(DataType a[],int n,int e);
    ~MGraph(){};
    void DFT(int v);
    void BFT(int v);
    private:
    DataType vertex[MaxSize];
    int visted[MaxSize];
    int edge[MaxSize][MaxSize];
    int vertexNum,edgeNum;
};
template <typename DataType>
MGraph<DataType>::MGraph(DataType a[],int n,int e)
{
    vertexNum = n;
    edgeNum = e;
    int w;
    for(int i = 1;i<=n;i++)
    {
        vertex[i] = a[i];
    }
    for(int i = 1;i<=n;i++)
    {
        visted[i] = 0;
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
        {
            cin>>edge[i][j];
        }
    }
}
template <typename DataType>
void MGraph<DataType>::DFT(int v)//深度優先搜索
{
    cout<<vertex[v];//輸出頂點具體信息
    visted[v] = 1;//修改標記
    for(int i = 1;i<=vertexNum;i++)//遍歷圖中的所有頂點
    {
        if(visted[i]!=1&&edge[v][i]<100)//如果與已知頂點相鄰並且沒有被訪問過
        DFT(i);//深度優先遍歷這個鄰接點
    }
}
template <typename DataType>
void MGraph<DataType>::BFT(int v)//廣度優先搜索
{
    queue<int>q;
    q.push(v);//起始頂點無條件入隊
    cout<<vertex[v];//輸出頂點信息
    visted[v] = 1;//修改起始點的訪問標識符
    while(!q.empty())//當隊列不空時
    {
        v = q.front();//取隊首元素
        q.pop();//刪除隊首元素
        for(int i = 1;i<=vertexNum;i++)//遍歷所有的頂點
        {
            if(visted[i]!=1&&edge[v][i]<100)//如果與已知頂點相鄰並且沒有被訪問過
            {
                cout<<vertex[i];
                visted[i] = 1;//修改訪問標誌
                q.push(i);//鄰接點入隊
            }
        }
    }
}

鄰接表的遍歷:
最小生成樹:(加點)
1.Prim算法
將圖分成兩部分,一部分爲待求最小生成樹,一部分爲沒有選過的點。
(1)起始最小生成樹爲空。
(2)將一個點加入到最小生成樹的集合中,遍歷所有與之相鄰的點,選擇權值最小的鄰接點加入最小生成樹集合中。
(3)遍歷與新加入頂點相鄰的點,選擇權值最小的鄰接點加入最小生成樹中,重複步驟2,直到所有點都加入最小生成樹中。

Prim算法的核心是記錄了目前未選取頂點與最小生成樹的最小權值,選完後更新記錄,直到算法完成。

#include<iostream>
#include<queue>
using namespace std;
const int MaxSize = 100;
int MinEdge(int a[],int n)//用於選取最小權值的邊
{
    int min = 100;
    int index = 1;
    for(int i = 1;i<=n;i++)
    {
        if(a[i]<=min&&a[i]!=0)
        {
            min = a[i];
            index = i;
        }
    }
    return index;
}
template <typename DataType>
class MGraph
{
    public:
    MGraph(DataType a[],int n,int e);
    ~MGraph(){};
    void Prim(int v);
    private:
    DataType vertex[MaxSize];
    int visted[MaxSize];
    int edge[MaxSize][MaxSize];
    int vertexNum,edgeNum;
};
template <typename DataType>
MGraph<DataType>::MGraph(DataType a[],int n,int e)
{
    vertexNum = n;
    edgeNum = e;
    int w;
    for(int i = 1;i<=n;i++)
    {
        vertex[i] = a[i];
    }
    for(int i = 1;i<=n;i++)
    {
        visted[i] = 0;
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
        {
            cin>>edge[i][j];
        }
    }
}
template <typename DataType>
void MGraph<DataType>::Prim(int v)
{
    int adjvex[MaxSize];//記錄與最小生成樹相鄰的頂點
    //adjvex[i] = j;表達的意思是整個圖中頂點i與生成樹中的頂點j相鄰
    int lowcost[MaxSize];//記錄與最小生成樹相鄰的頂點的最小權值
    //lowcost[i] = w;表達的意思是整個圖中頂點i與生成樹的最小權值是w
    for(int i = 1;i<=vertexNum;i++)
    {
        lowcost[i] = edge[v][i];
        adjvex[i] = v;//初始圖中所有頂點與指定頂點相鄰
    }
    lowcost[v] = 0;//將指定頂點加入到最小生成樹中
    int j;
    for(int k = 1;k<vertexNum;k++)
    {
        j = MinEdge(lowcost,vertexNum);//選出最小值的點
        cout<<lowcost[j]<<" ";
        lowcost[j] = 0;
        for(int i = 1;i<=vertexNum;i++)//遍歷所有的頂點更新記錄
        {
            if(edge[i][j]<lowcost[i])
            {
                lowcost[i] = edge[i][j];
                adjvex[i] = j;
            }
        }
    }
}

2.Kruskal算法(加邊)
(1)對圖中的所有邊進行排序;
(2)初始化最小生成樹爲包括所有點,0條邊的樹
(3)如果最小權值邊與生成樹不在同一連通分量,加入最小生成樹中
(4)如果在同一連通分量中,略過這條邊
(5)重複步驟4和5,直到選出頂點數-1條邊

使用邊集數組存儲邊,應用並查集合並。

合併兩棵樹:構建父子關係

判斷兩棵樹是否爲同一連通分量的方案:檢查它們的根是否相同

#include <iostream>
#include <algorithm>
using namespace std;
struct Edge{//定義邊集
	int from,to,weight;
};
bool cmp(Edge&e1,Edge&e2){
	return e1.weight<e2.weight;
}
class EdgeSet
{
	private:
	Edge*edge;
	int vertexNum;
	int edgeNum;
	int FindRoot(int*parent,int index);//根查找函數
	public:
	EdgeSet(int n,int e);
	~EdgeSet(){
		
	};
	void Kruskal();
};
EdgeSet::EdgeSet(int n,int e){
	vertexNum = n;
	edgeNum = e;
	edge = new Edge[edgeNum];
	int index = 0;
	int a[vertexNum+1][vertexNum+1];
	for(int i = 1;i<=vertexNum;i++)
		for(int j = 1;j<=vertexNum;j++){
		cin>>a[i][j];
	}
	for(int i = 1;i<=vertexNum;i++)
		for(int j = i;j<=vertexNum;j++){
		if(a[i][j]<100&&a[i][j]>0){
			edge[index].from = i;
			edge[index].to = j;
			edge[index].weight = a[i][j];
			index++;
		}
	}
	sort(edge,edge+index,cmp);
}
int EdgeSet::FindRoot(int*parent,int index){
	int root = index;
	while(parent[root]>-1){
		root = parent[root];
	}
	return root;
}
void EdgeSet::Kruskal(){
	int*parent = new int[vertexNum+1];
	for(int i = 0;i<=vertexNum;i++)
		parent[i] = -1;//所有的頂點均沒有雙親
	int count = 0;//記錄邊數
	for(int k = 0;k<edgeNum;k++){
		int i = FindRoot(parent,edge[k].from);
		int j = FindRoot(parent,edge[k].to);
		if(i!=j){//判斷是否爲同一連通分量
			cout<<edge[k].from<<" "<<edge[k].to<<" ";
			parent[j] = i;//合併
			count++;
			if(count==vertexNum-1)
				break;
		}
	}
}

最短路徑算法(單源點到所有點)
1.Dijkstra算法:
在與指定單源點鄰接的所有點中,必有一條最短路徑長度是兩點之間的權值。因此最短路徑可以分成兩種情況:一種是已經存在的路徑,另一種是經過其他的頂點構建的新的路徑
(1)初始路徑爲單源點相鄰的頂點的權值,如果沒有鄰接則用一個默認的最大值代替
(2)選擇最小權值的點
(3)更新最短路徑長度

void MGraph::Dijkstra(int v){
	int*dist = new int[vertexNum];//記錄單源點到各頂點的路徑長度,dist[i]:單源點到第i點的路徑長度
	string*path = new string[edgeNum];//用於打印路徑具體情況
	for(int i = 0;i<vertexNum;i++){//初始化
		dist[i] = edge[v][i];
		if(dist[i]!=MaxSize)
			path[i] = vertex[v]+" "+vertex[i];
		else
			path[i] = "";
	}
	int index;
	for(int num = 1;num<vertexNum;num++){
		index = Min(dist,vertexNum);//選擇
		for(int i = 0;i<vertexNum;i++){//嘗試所有的點
			if(dist[i]>dist[index]+edge[index][i]){//更新
				dist[i] = dist[index]+edge[index][i];
				path[i] = path[index]+" "+vertex[i];
			}
		}
		dist[index] = 0;//加入最短路徑
	}
}

2.Floyd算法:
相對於Dijkstra更樸素的算法,走所有的路,選最短的那條。
n與n個點之間的所有路徑,可以調用n次Dijkstra算法等價實現

void MGraph::Floyd(){
	int dist[MaxSize][MaxSize];//記錄路徑長度
	string path[MaxSize][MaxSize];
	for(int i = 0;i<vertexNum;i++){//初始化路徑長度爲權值大小
		for(int j = 0;j<vertexNum;j++){
			dist[i][j] = edge[i][j];
			if(edge[i][j]!=MaxSize&&i!=j){
				path[i][j] = vertex[i]+" "+vertex[j];
			}
			else
				path[i][j] = "";
		}
	}
	for(int k = 0;k<vertexNum;k++){//在第i個點和第j個點之間嘗試所有的點
		for(int i = 0;i<vertexNum;i++){
			for(int j = 0;j<vertexNum;j++){
				if(dist[i][j]>dist[i][k]+dist[k][j]){//更新
					dist[i][j] = dist[i][k]+dist[k][j];
					path[i][j] = path[i][k]+" "+path[k][j].substr(3);
				}
			}
		}
	}
}

拓撲排序:
先說一下拓撲排序的概念:
對一個有向無環圖G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。
———出自百度
算法描述:
(1)選擇沒有箭頭指入的節點中的任一個,可以作爲合法序列的起點,放入序列。
(2)當我們將某個節點放入序列後,去掉該節點以及從該節點指出的所有箭頭。在新的圖中,選擇沒有箭頭指向的節點,作爲序列中的下一個點。
(3) 重複上面的過程,直到所有的節點都被放入序列。

可以用隊列實現以上算法
(1)如果頂點的入度爲0,加入隊列中
(2)當隊列不爲空時
a.訪問隊首元素
b.將所有與隊首元素相連的所有點入度減一
c.檢查鄰接點的入度是否爲0,爲0入隊

#include <iostream>
#include <string.h>
#include <stack>
#include <sstream>
using namespace std;
string itos(int i){
	stringstream s;
	s<<i;
	return s.str();
}
struct EdgeNode{
	int index;
	EdgeNode*next;
};
struct Vertex{
	string vertex;
	int in;
	EdgeNode*first;
};
class AdjList{
	private:
	Vertex*v;
	int vertexNum;
	int edgeNum;
	public:
	AdjList(int n,int e);
	~AdjList();
	void TopSort();
};
AdjList::AdjList(int n,int e){
	vertexNum = n;
	edgeNum = e;
	v = new Vertex[vertexNum+1];
	for(int i = 1;i<=vertexNum;i++){
	    v[i].in = 0;
	    v[i].first = NULL;
	    v[i].vertex = "v"+itos(i);
	}
	int from,to;
	for(int i = 1;i<=edgeNum;i++){
		cin>>from>>to;
		EdgeNode*s = new EdgeNode;
		s->index = to;
		s->next = v[from].first;
		v[from].first = s;
		v[to].in++;
	}
}
AdjList::~AdjList(){
	EdgeNode*p = NULL;
	EdgeNode*q = NULL;
	for(int i = 1;i<=vertexNum;i++){
		p = q = v[i].first;
		while(p!=NULL){
			p = p->next;
			delete q;
			q = p;
		}
	}
}
void AdjList::TopSort(){
	stack<Vertex>s;
	for(int i = vertexNum;i>0;i--)
		if(v[i].in==0)
		s.push(v[i]);
	while(!s.empty()){
		cout<<s.top().vertex<<" ";
		EdgeNode*p = s.top().first;
		s.pop();
		while(p!=NULL){
			v[p->index].in--;
			if(v[p->index].in==0)
				s.push(v[p->index]);
			p = p->next;
		}
	}
}

關鍵路徑:

struct Edge{
	int from,to,e,l;
};
class AOE{
	private:
	int edgeNum;
	int vertexNum;
	Edge*edgeset;//邊集
	int edge[MaxSize][MaxSize];//鄰接矩陣
	string*vertex;//點集
	public:
	AOE(int n,int e);
	~AOE(){
		
	};
	void CriPath();
};
AOE::AOE(int n,int e){
	vertexNum = n;
	edgeNum = e;
	edgeset = new Edge[edgeNum+1];
	vertex = new string[vertexNum+1];
	int w,from,to;
	for(int i = 1;i<=vertexNum;i++){
		vertex[i] = 'v'+itos(i);
		for(int j = 1;j<=vertexNum;j++){
			edge[i][j] = MaxSize;
		}
	}
	for(int i = 1;i<=edgeNum;i++){
		cin>>from>>to>>w;
		edge[from][to] = w;
		edgeset[i].from = from;
		edgeset[i].to = to;
		edgeset[i].e = 0;
		edgeset[i].l = 0;
	}
}
void AOE::CriPath(){
	int visit[vertexNum+1] = {
		0
	};
	int vl[vertexNum+1] = {
		MaxSize
	};//事件最早發生時間
	int ve[vertexNum+1] = {
		0
	};//事件最晚發生時間
	ve[1] = 0;
	queue<int>q;
	q.push(1);
	int i;
	visit[1] = 1;
	while(!q.empty()){
		i = q.front();
		q.pop();
		for(int j = 1;j<=vertexNum;j++){
			if(edge[i][j]!=MaxSize&&ve[j]<edge[i][j]+ve[i])
				ve[j] = edge[i][j]+ve[i];
			if(!visit[j])
				q.push(j);
			visit[j] = 1;
		}
	}
	q.push(vertexNum);
	for(int i = 1;i<=vertexNum;i++){
		visit[i] = 0;
		vl[i] = ve[vertexNum];
	}
	while(!q.empty()){
		i = q.front();
		//cout<<i<<" ";
		q.pop();
		for(int j = vertexNum;j>0;j--){
		//for(int j = 1;j<=vertexNum;j++){
			if(edge[j][i]!=MaxSize&&vl[j]>vl[i]-edge[j][i]){
				vl[j] = vl[i]-edge[j][i];
			}
			if(!visit[j])
				q.push(j);
			visit[j] = 1;
		}
	}
	for(int i = 1;i<=edgeNum;i++){
		edgeset[i].e = ve[edgeset[i].from];
		edgeset[i].l = vl[edgeset[i].to]-edge[edgeset[i].from][edgeset[i].to];
		if(edgeset[i].e==edgeset[i].l)
		cout<<vertex[edgeset[i].from]<<" ";//<<vertex[edgeset[i].to]<<" ";
	}
	cout<<vertex[vertexNum];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章