數據結構總結(圖)

  圖這裏存的就是複雜數據了,算法普遍較難,並有很多細節需要經常看。

本章主要內容有,圖的邏輯結構,存儲結構,連通性,最小生成樹,最短路徑,AOV,AOE 網等問題。

一,圖的邏輯結構

圖的部分知識在離散數學已經有所介紹,如有向圖頂點度(入度=出度=邊數),注意有向圖連通也是任意倆點之間連通,不是無向圖有邊串連所有點的形式。

稀疏圖:稱邊數很少的圖爲稀疏圖;

稠密圖:稱邊數很多的圖爲稠密圖

連通分量:非連通圖的極大連通子圖稱爲連通分量。

生成樹:n個頂點的連通圖G的生成樹是包含G全部頂點的一個極小連通子圖。

eg:有7個頂點保證圖在任何情況下都連通,最少要多少條邊?

16條,6個點做完全圖,一個點連上

   圖的遍歷有深度優先搜索(棧輔助)、廣度優先搜索(隊輔助)兩種主要方式。

二,圖的存儲結構

1,鄰接矩陣(數組表達法)

 關於頂點元素的集合一定用順序存儲。

注意無向圖在構造鄰接矩陣是對稱的edge[i][j]=edge[j][i]=1;

DFS針對連通圖的遍歷
int visited[MaxSize];
template <class T>
void MGraph::DFSTraverse(int v){
     cout<<vertex[v]; visited [v]=1;//記錄是否被訪問過
     for (j=0; j<vertexNum; j++)
         if (arc[v][j]==1 && visited[j]==0)
            DFSTraverse( j );
}
///////////////////////////////////////////////////////////
BFS
int visited[MaxSize];
template <class T>
void MGraph::BFSTraverse(int v){     
    front=rear=-1;
    cout<<vertex[v]; visited[v]=1;  Q[++rear]=v; 
    while (front!=rear)
     {
         v=Q[++front];   
         for (j=0; j<vertexNum; j++)
            if (arc[v][j]==1 && visited[j]==0 ) {
                  cout<<vertex[j];visited[j]=1;
                  Q[++rear]=j;
            }
      }
}

2,鄰接表(出邊表)

對於圖的每個頂點vi,將所有鄰接於vi的頂點鏈成一個單鏈表,稱爲頂點vi的邊表(對於有向圖則稱爲出邊表)

所有邊表的頭指針和存儲頂點信息的一維數組構成了頂點表。

struct ArcNode{   
      int adjvex; 
      ArcNode *next;
};

template <class T>
struct VertexNode{
      T vertex;
      ArcNode *firstedge;//初始化時賦空
};
鏈表的DFS
template <class T>
void ALGraph::DFSTraverse(int v){        
    cout<<adjlist[v].vertex;  visited[v]=1;
    p=adjlist[v].firstedge;    
    while (p!=NULL)     {
        j=p->adjvex;
        if (visited[j]==0) DFSTraverse(j);
    p=p->next;           
    }
}


鏈表的BFS
template <class T>
void ALGraph::BFSTraverse(int v){
   front=rear=-1;   
   cout<<adjlist[v].vertex;    visited[v]=1;   Q[++rear]=v;   
   while (front!=rear)  {
       v=Q[++front];    p=adjlist[v].firstedge;    
       while (p!=NULL)  {
            j= p->adjvex;
            if (visited[j]==0) {
                cout<<adjlist[j].vertex;  visited[j]=1; Q[++rear]=j;
            }
            p=p->next;
       }
    }
}

3,圖的其餘存儲方法還有有向圖的十字鏈表法、無向圖的鄰接多重表、邊集數組。

邊集數組:對邊依次進行處理時用、最小代價生成樹

利用兩個一維數組

一個數組存儲頂點信息,
另外一個數組存儲邊及其權

數組分量包含三個域:邊所依附的兩個頂點,權值

三,最小生成樹,連通所有點,權值最小

1,prim算法(加點法,適用稠密圖)

int minedge(int lowcost[],int n){
    int m=100,p;
    for(int i=0;i<n;i++)
    if(lowcost[i]!=0&&m>lowcost[i]){
    m=lowcost[i];
    p=i;
    }
    return p;}
Void prime(MGraph G){
    for(int i=1;i<G.vertexNu;i++){     
        lowcost[i]=G.arc[0][i];  adjvex[i]=0;//從v0開始生成樹,low cost記錄與v0鄰接邊的權
    }
    lowcost[0]=0;
    for(i=1;i<G.vertexNum;i+++){
        k=MinEdge(lowcost,G.vertexNum)//找最小權
        cout<<K<<adjvex[k]<<lowcost[k];
        lowcost[k]=0;                  //將找到的點列入樹中
              for(j=1;j<G.vertexNum;j++)
          if((G.arc[k][j]<lowcost[j]){
              lowcost[j]=G.arc[k][j];//更新關於樹的結點權
              arcvex[j]=k;
           }
    }
}

2,kruskal算法(加邊法,稀疏圖)

使用邊集數組,並查集,parent數組判斷是否倆點屬於同一個連通分量,不屬於後一樹的parent變前一樹的值。

#include<bits/stdc++.h>
using namespace std;
struct bian                  //邊集數組
{
    int from;
    int to;
    int weight;
};
bool cmp(bian a,bian b)           //使sort按權值排序
{
    return a.weight<b.weight;
}
int findd(int *parent,int o)     //找根結點
{
    int ff;
    ff=o;
    while(parent[ff]>-1)
        ff=parent[ff];
    return ff;
}
int main()
{
    int m,n;
    cin>>n>>m;
    int parent[99];
    int vis[99][99];
    bian eg[99];
    int f=0;
    for(int i=0; i<n; i++)
        parent[i]=-1;         //初始都是單個點,n個樹
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
            vis[i][j]=0;      //都沒訪問過
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
        {
            int p;
            cin>>p;
            if(p!=0&&p!=100&&vis[i][j]==0)
            {
                eg[f].from=i;
                eg[f].to=j;
                eg[f].weight=p;
                vis[i][j]=1;           //無向圖
                vis[j][i]=1;
                f++;
            }
        }
    sort(eg,eg+f,cmp);
    int k=0,beginn,endd,countt=0;
    for(k=0; k<f; k++)
    {
        beginn=eg[k].from;
        endd=eg[k].to;
        int x,y;
        x=findd(parent,beginn);
        y=findd(parent,endd);
        if(x!=y)
        {
            cout<<beginn+1<<" "<<endd+1<<" ";//輸出結點
            parent[y]=x;
            countt++;
            if(countt==n-1)
                break;
        }
    }

}

四,最短路徑

在網圖中,最短路徑是指兩頂點之間經歷的邊上權值之和最短的路徑。

1,dijkstra算法(單元點到其他頂點的最短路徑,不能解決負權問題)

路徑長度遞增,解n-1條路的數值

數組dist[n]每個分量dist[i]表示當前所找到的從始點v到終點vi的最短路徑的長度。初態爲:

  若從vvi有弧,則dist[i]爲弧上權值;否則置dist[i]爲∞

數組path[n]path[i]是一個字符串,表示當前所找到的從始點v到終點vi的最短路徑。初態爲:若從vvi有弧,則path[i]vvi;否則置path[i]空串。

數組s[n]存放源點和已經找到最短路徑的終點,其初態爲只有一個源點v

#include<iostream>
#include<string>
#include<cmath>
using namespace std;
const int MaxSize=10;
const int Max=1000;
class MGraph
{
public:
    MGraph(int n,int e);
    void Dijkstra(int v,int x);
private:
    int vertexnum;
    int arcnum;
    int arc[MaxSize][MaxSize];
    string vertex[MaxSize];
};
MGraph::MGraph(int n,int e){
    vertexnum=n;
    arcnum=e;
    for(int i=0;i<vertexnum;i++){       字符串與整形相結合存入vertex
        vertex[i]="v";
        vertex[i]+=(i+'0');
    }
    for(int i=0;i<vertexnum;i++)
        for(int j=0;j<vertexnum;j++)
        arc[i][j]=1000;

    int i,j,num;
    for(int k=0;k<arcnum;k++){
        cin>>i>>j>>num;
        arc[i][j]=num;
    }
}
void MGraph::Dijkstra(int v,int x){
    string s[Max];
    string path[Max];
    int dist[Max];
    for(int i=0;i<vertexnum;i++){
        s[i]="";              
        dist[i]=arc[v][i];               //存儲相鄰點的權值
        if(dist[i]!=Max)
            path[i]=vertex[v]+" "+vertex[i];   // 存儲可能的路徑
        else
            path[i]="";
    }
    s[v]=vertex[v];                  //存儲每條最短最短路徑的終點可寫成s[0]=vertex[v];
    int num=1;
    while(num<vertexnum){
        int k=0;
        for(int i=0;i<vertexnum;i++)
            if(dist[i]<dist[k]&&s[i]=="") k=i;
        s[k]=vertex[k];                           //s[num++]=vetex[k];
        num++;
        for(int i=0;i<vertexnum;i++)
        if(dist[k]+arc[k][i]<dist[i]){
            dist[i]=dist[k]+arc[k][i];
            path[i]=path[k]+" "+vertex[i];
        }
    }
    if(path[x]!=""){
        cout<<dist[x]<<endl;
        cout<<path[x]<<endl;
    }
    else
        cout<<"no answer"<<endl;
}
int main()
{
    int n,e;
    int v,x;
    cin>>n>>e;
    cin>>v>>x;
    MGraph mg(n,e);
    mg.Dijkstra(v,x);
}

2,floyd算法(任意一對節點之間的最短路徑)

鄰接矩陣存儲,迭代進行

#include<iostream>
#include<vector>
using namespace std;
  int path[99][99];
void judge(int x,int y)
{
     int k=path[x][y];
     if(k==-1) return;
	 judge(x,k);
	 cout<<"v"<<k<<" ";
	 judge(k,y);
}

int main(){
    int n,m,v,e;
    cin>>n>>m>>v>>e;
    int dist[99][99];


    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++){
        dist[i][j]=10000;
		path[i][j]=-1;}

    while(m--){
            int x,y,z;
        cin>>x>>y>>z;
    dist[x][y]=z;
    }

    for(int k=0;k<n;k++)
     for(int j=0;j<n;j++)
        for(int i=0;i<n;i++){
	if(k==j || k==i || i==j) continue;
        if(dist[i][k]+dist[k][j]<dist[i][j]){
            dist[i][j]=dist[i][k]+dist[k][j];
            path[i][j]=k;
        }}
	bool Flag=false;

         if(dist[v][e]!=10000)
		 {
			 Flag=true;
             cout<<dist[v][e]<<endl;
			 cout<<"v"<<v<<" ";
             judge(v,e);
			 cout<<"v"<<e<<" "<<endl;
		 }


   if(Flag==false)
   cout<<"no answer"<<endl;

}

五,有向無環圖及其應用。

1,AOV網

在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關係,稱這樣的有向圖爲頂點表示活動的網,簡稱AOV網。

拓撲序列(前驅後繼關係都滿足)

若從頂點vivj有一條路徑,則在頂點的拓撲序列中頂點vi必在頂點vj之前

拓撲排序:對一個有向圖構造拓撲序列的過程稱爲拓撲排序

#include<bits/stdc++.h>
using namespace std;
struct bian{
    int hao;
bian *e;
};
struct dian{
int ru;
int  t;
bian *first;
};
int main(){
int v,a;
cin>>v>>a;
dian dd[v+1];
for(int i=1;i<=v;i++)          //邊集數組,單存一個入度
{
    dd[i].ru=0;
    dd[i].t=i;
    dd[i].first=NULL;
}
for(int i=0;i<a;i++)
{
    int x,y;
    bian *s;
    cin>>x>>y;
    dd[y].ru++;
    s=new bian;
    s->hao=y;
    s->e=dd[x].first;
    dd[x].first=s;
}

stack<int>p;                 /用棧進行操作
for(int i=v;i>=1;i--)
    if(dd[i].ru==0)
    p.push(i);
while(!p.empty()){
    int j=p.top();
    p.pop();
    cout<<"v"<<dd[j].t<<" ";
   bian *q;
    q=dd[j].first;
    while(q!=NULL){
        int k=q->hao;
        dd[k].ru--;
        if(dd[k].ru==0)
            p.push(k);
        q=q->e;
    }
}



}

2,AOE網

在一個表示工程的帶權有向圖中,

用頂點表示事件,

用有向邊表示活動,

邊上的權值表示活動的持續時間,

稱這樣的有向圖叫做邊表示活動的網,簡稱AOE

AOE網中沒有入邊的頂點稱爲始點(或源點),沒有出邊的頂點稱爲終點(或匯點)。

AOE網的性質:

⑴ 只有在某頂點所代表的事件發生後,從該頂點出發的各活動才能開始

⑵ 只有在進入某頂點的各活動都結束,該頂點所代表的事件才能發生

 

 

關鍵路徑:AOE網中,從始點到終點具有最大路徑長度(該路徑上的各個活動所持續的時間之和)的路徑稱爲關鍵路徑。

關鍵活動:關鍵路徑上的活動稱爲關鍵活動。

⑴ 事件的最早發生時間ve[k], 指從始點開始到頂點vk的最大路徑長度。這個長度決定了所有從頂點vk發出的活動能夠開工的最早時間。

⑵ 事件的最遲發生時間vl[k] ,指在不推遲整個工期的前提下,事件vk允許的最晚發生時間。

⑶ 活動的最早開始時間e[i]

⑷ 活動的最晚開始時間l[i]

#include<bits/stdc++.h>
using namespace std;
struct Edge{                       //構建邊的結構體,表式邊的倆點,邊上事件的最早最晚發生時間
	int from;
	int to;
	int e;
	int l;
};
int ve[99];
int vl[99];

	int vertexnum;
	int adjlist[99][99];   
	int start,end;
	Edge edge[99];  

int main(){
    int v,a;
    int visit[99];
    cin>>v>>a;

    vertexnum=v;


    for(int i=1;i<=v;i++)
        for(int j=1;j<=v;j++)
        adjlist[i][j]=9999;
         for(int i=1;i<=a;i++){
        int x,y,z;
        cin>>x>>y>>z;
        adjlist[x][y]=z;
        edge[i].from=x;
        edge[i].to=y;

    }
    queue<int>q;
    q.push(1);//源點事件入隊
	for(int j=1;j<=vertexnum;j++)	{  
		ve[j]=0;	visit[j]=0;	}
	visit[1]=1;
     while(!q.empty())	{
		int i=q.front();       
		q.pop();
		for(int j=1;j<=vertexnum;j++){//計算i的鄰接點的ve
			if(adjlist[i][j]!=9999 && ve[i]+adjlist[i][j]>ve[j] ){
				ve[j]=ve[i]+adjlist[i][j];    //算最早,找大的
				if(!visit[j])   
					q.push(j);
				visit[j]=1;
			}
		}
	}
	//for(int i=1;i<=v;i++)
       // cout<<ve[i]<<" ";
    //cout<<endl;
/////////////////////////////////////////////
      q.push(vertexnum);
	for(int j=1;j<=vertexnum;j++)	{
		vl[j]=ve[vertexnum];	visit[j]=0;	}
    while(!q.empty())	{
		int i=q.front();
		q.pop();
		for(int j=1;j<=vertexnum;j++)	{
			if(adjlist[j][i]!=9999 && vl[i]-adjlist[j][i]<vl[j] ){
				vl[j]=vl[i]-adjlist[j][i];
				if(!visit[j])
					q.push(j);
				visit[j]=1;
			}
		}
	}
	//for(int i=1;i<=v;i++)
      //  cout<<vl[i]<<" ";
    //cout<<endl;
	//////////////////////////////////

int f=0;
for(int i=1;i<=a;i++)
	{
		edge[i].e=ve[edge[i].from];      //根據節點算邊,找相等的是關鍵路徑
		edge[i].l=vl[edge[i].to]-adjlist[edge[i].from][edge[i].to];
		if(edge[i].e==edge[i].l){
                if(f==0){
			cout<<"v"<<edge[i].from<<" "<<"v"<<edge[i].to<<" ";
			f=1;}
			else
                cout<<"v"<<edge[i].to<<" ";
		}

	}
	cout<<endl;
/*for(int i=1;i<=a;i++)
	cout<<edge[i].e<<" ";
	cout<<endl;
	for(int i=1;i<=a;i++)
	cout<<edge[i].l<<" ";*/
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章