1. 用c++寫出圖的遍歷演算法,謝謝!
ude <iostream>
#include <malloc.h>
using namespace std;
#define int_max 10000
#define inf 9999
#define max 20
//…………………………………………鄰接矩陣定義……………………
typedef struct ArcCell
{
int adj;
char *info;
}ArcCell,AdjMatrix[20][20];
typedef struct
{
char vexs[20];
AdjMatrix arcs;
int vexnum,arcnum;
}MGraph_L;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
int localvex(MGraph_L G,char v)//返回V的位置
{
int i=0;
while(G.vexs[i]!=v)
{
++i;
}
return i;
}
int creatMGraph_L(MGraph_L &G)//創建圖用鄰接矩陣表示
{
char v1,v2;
int i,j,w;
cout<<"…………創建無向圖…………"<<endl<<"請輸入圖G頂點和弧的個數:(4 6)不包括「()」"<<endl;
cin>>G.vexnum>>G.arcnum;
for(i=0;i!=G.vexnum;++i)
{
cout<<"輸入頂點"<<i<<endl;
cin>>G.vexs[i];
}
for(i=0;i!=G.vexnum;++i)
for(j=0;j!=G.vexnum;++j)
{
G.arcs[i][j].adj=int_max;
G.arcs[i][j].info=NULL;
}
for(int k=0;k!=G.arcnum;++k)
{
cout<<"輸入一條邊依附的頂點和權:(a b 3)不包括「()」"<<endl;
cin>>v1>>v2>>w;//輸入一條邊依附的兩點及權值
i=localvex(G,v1);//確定頂點V1和V2在圖中的位置
j=localvex(G,v2);
G.arcs[i][j].adj=w;
G.arcs[j][i].adj=w;
}
cout<<"圖G鄰接矩陣創建成功!"<<endl;
return G.vexnum;
}
void ljjzprint(MGraph_L G)
{
int i,j;
for(i=0;i!=G.vexnum;++i)
{
for(j=0;j!=G.vexnum;++j)
cout<<G.arcs[i][j].adj<<" ";
cout<<endl;
}
}
int visited[max];//訪問標記
int we;
typedef struct arcnode//弧結點
{
int adjvex;//該弧指向的頂點的位置
struct arcnode *nextarc;//弧尾相同的下一條弧
char *info;//該弧信息
}arcnode;
typedef struct vnode//鄰接鏈表頂點頭接點
{
char data;//結點信息
arcnode *firstarc;//指向第一條依附該結點的弧的指針
}vnode,adjlist;
typedef struct//圖的定義
{
adjlist vertices[max];
int vexnum,arcnum;
int kind;
}algraph;
//…………………………………………隊列定義……………………
typedef struct qnode
{
int data;
struct qnode *next;
}qnode,*queueptr;
typedef struct
{
queueptr front;
queueptr rear;
}linkqueue;
//………………………………………………………………………
typedef struct acr
{
int pre;//弧的一結點
int bak;//弧另一結點
int weight;//弧的權
}edg;
int creatadj(algraph &gra,MGraph_L G)//用鄰接表存儲圖
{
int i=0,j=0;
arcnode *arc,*tem,*p;
for(i=0;i!=G.vexnum;++i)
{
gra.vertices[i].data=G.vexs[i];
gra.vertices[i].firstarc=NULL;
}
for(i=0;i!=G.vexnum;++i)
{
for(j=0;j!=G.vexnum;++j)
{
if(gra.vertices[i].firstarc==NULL)
{
if(G.arcs[i][j].adj!=int_max&&j!=G.vexnum)
{
arc=(arcnode *)malloc(sizeof(arcnode));
arc->adjvex=j;
gra.vertices[i].firstarc=arc;
arc->nextarc=NULL;
p=arc;
++j;
while(G.arcs[i][j].adj!=int_max&&j!=G.vexnum)
{
tem=(arcnode *)malloc(sizeof(arcnode));
tem->adjvex=j;
gra.vertices[i].firstarc=tem;
tem->nextarc=arc;
arc=tem;
++j;
}
--j;
}
}
else
{
if(G.arcs[i][j].adj!=int_max&&j!=G.vexnum)
{
arc=(arcnode *)malloc(sizeof(arcnode));
arc->adjvex=j;
p->nextarc=arc;
arc->nextarc=NULL;
p=arc;
}
}
}
}
gra.vexnum=G.vexnum;
gra.arcnum=G.arcnum;
/*for(i=0;i!=gra.vexnum;++i)
{
arcnode *p;
cout<<i<<" ";
p=gra.vertices[i].firstarc;
while(p!=NULL)
{
cout<<p->adjvex;
p=p->nextarc;
}
cout<<endl;
}*/
cout<<"圖G鄰接表創建成功!"<<endl;
return 1;
}
void adjprint(algraph gra)
{
int i;
for(i=0;i!=gra.vexnum;++i)
{
arcnode *p;
cout<<i<<" ";
p=gra.vertices[i].firstarc;
while(p!=NULL)
{
cout<<p->adjvex;
p=p->nextarc;
}
cout<<endl;
}
}
int firstadjvex(algraph gra,vnode v)//返回依附頂點V的第一個點
//即以V為尾的第一個結點
{
if(v.firstarc!=NULL)
return v.firstarc->adjvex;
}
int nextadjvex(algraph gra,vnode v,int w)//返回依附頂點V的相對於W的下一個頂點
{
arcnode *p;
p=v.firstarc;
while(p!=NULL&&p->adjvex!=w)
{
p=p->nextarc;
}
if(p->adjvex==w&&p->nextarc!=NULL)
{
p=p->nextarc;
return p->adjvex;
}
if(p->adjvex==w&&p->nextarc==NULL)
return -10;
}
int initqueue(linkqueue &q)//初始化隊列
{
q.rear=(queueptr)malloc(sizeof(qnode));
q.front=q.rear;
if(!q.front)
return 0;
q.front->next=NULL;
return 1;
}
int enqueue(linkqueue &q,int e)//入隊
{
queueptr p;
p=(queueptr)malloc(sizeof(qnode));
if(!p)
return 0;
p->data=e;
p->next=NULL;
q.rear->next=p;
q.rear=p;
return 1;
}
int dequeue(linkqueue &q,int &e)//出隊
{
queueptr p;
if(q.front==q.rear)
return 0;
p=q.front->next;
e=p->data;
q.front->next=p->next;
if(q.rear==p)
q.rear=q.front;
free(p);
return 1;
}
int queueempty(linkqueue q)//判斷隊為空
{
if(q.front==q.rear)
return 1;
return 0;
}
void bfstra(algraph gra)//廣度優先遍歷
{
int i,e;
linkqueue q;
for(i=0;i!=gra.vexnum;++i)
visited[i]=0;
initqueue(q);
for(i=0;i!=gra.vexnum;++i)
if(!visited[i])
{ visited[i]=1;
cout<<gra.vertices[i].data;
enqueue(q,i);
while(!queueempty(q))
{
dequeue(q,e);
// cout<<" "<<e<<" ";
for(we=firstadjvex(gra,gra.vertices[e]);we>=0;we=nextadjvex(gra,gra.vertices[e],we))
{
if(!visited[we])
{
visited[we]=1;
cout<<gra.vertices[we].data;
enqueue(q,we);
}
}
}
}
}
int dfs(algraph gra,int i);//聲明DFS
int dfstra(algraph gra)
{
int i,j;
for(i=0;i!=gra.vexnum;++i)
{
visited[i]=0;
}
for(j=0;j!=gra.vexnum;++j)
{
if(visited[j]==0)
dfs(gra,j);
}
return 0;
}
int dfs(algraph gra,int i)
{
visited[i]=1;
int we1;
// cout<<i<<visited[i]<<endl;
cout<<gra.vertices[i].data;
// cout<<endl;
for(we=firstadjvex(gra,gra.vertices[i]);we>=0;we=nextadjvex(gra,gra.vertices[i],we))
{
// cout<<we<<visited[we]<<endl;
we1=we;
// cout<<nextadjvex(gra,gra.vertices[i],we)<<endl;
if(visited[we]==0)
// cout<<
dfs(gra,we);//<<endl;
// cout<<i<<we1<<endl;
we=we1;
// cout<<nextadjvex(gra,gra.vertices[i],we)<<endl;
}
return 12;
}
int bfstra_fen(algraph gra)//求連通分量
{
int i,j;
for(i=0;i!=gra.vexnum;++i)
{
visited[i]=0;
}
for(j=0;j!=gra.vexnum;++j)
{
if(visited[j]==0)
{
dfs(gra,j);
cout<<endl;
}
}
return 0;
}
typedef struct
{
int adjvex;
int lowcost;
}closedge;
/*int minimum(closedge *p);
int minispantree(MGraph_L G,char u)
{
int k,j,i;
closedge closedge_a[20];
k=localvex(G,u);
// cout<<k<<endl;
for(j=0;j!=G.vexnum;++j)
{
if(j!=k)
{
closedge_a[j].adjvex=u;
closedge_a[j].lowcost=G.arcs[k][j].adj;
}
for(i=1;i!=G.vexnum;++i)
{
k=minimum(closedge_a);
cout<<k;
cout<<closedge_a[k].adjvex<<" "<<G.vexs[k]<<endl;
closedge_a[k].lowcost=0;
for(j=0;j!=G.vexnum;++j)
if(G.arcs[k][j].adj<closedge_a[j].lowcost)
{
closedge_a[j].adjvex=G.vexs[k];
closedge_a[j].lowcost=G.arcs[k][j].adj;
}
}
}
return 0;
}
int minimum(closedge *p)
{
int s=10000;
for(;p!=NULL;++p)
{
if(s>p->lowcost)
s=p->lowcost;
}
return s;
}*/
int prim(int g[][max],int n) //最小生成樹PRIM演算法
{
int lowcost[max],prevex[max]; //LOWCOST[]存儲當前集合U分別到剩餘結點的最短路徑
//prevex[]存儲最短路徑在U中的結點
int i,j,k,min;
for(i=2;i<=n;i++) //n個頂點,n-1條邊
{
lowcost[i]=g[1][i]; //初始化
prevex[i]=1; //頂點未加入到最小生成樹中
}
lowcost[1]=0; //標志頂點1加入U集合
for(i=2;i<=n;i++) //形成n-1條邊的生成樹
{
min=inf;
k=0;
for(j=2;j<=n;j++) //尋找滿足邊的一個頂點在U,另一個頂點在V的最小邊
if((lowcost[j]<min)&&(lowcost[j]!=0))
{
min=lowcost[j];
k=j;
}
printf("(%d,%d)%d\t",prevex[k]-1,k-1,min);
lowcost[k]=0; //頂點k加入U
for(j=2;j<=n;j++) //修改由頂點k到其他頂點邊的權值
if(g[k][j]<lowcost[j])
{
lowcost[j]=g[k][j];
prevex[j]=k;
}
printf("\n");
}
return 0;
}
int acrvisited[100];//kruscal弧標記數組
int find(int acrvisited[],int f)
{
while(acrvisited[f]>0)
f=acrvisited[f];
return f;
}
void kruscal_arc(MGraph_L G,algraph gra)
{
edg edgs[20];
int i,j,k=0;
for(i=0;i!=G.vexnum;++i)
for(j=i;j!=G.vexnum;++j)
{
if(G.arcs[i][j].adj!=10000)
{
edgs[k].pre=i;
edgs[k].bak=j;
edgs[k].weight=G.arcs[i][j].adj;
++k;
}
}
int x,y,m,n;
int buf,edf;
for(i=0;i!=gra.arcnum;++i)
acrvisited[i]=0;
for(j=0;j!=G.arcnum;++j)
{
m=10000;
for(i=0;i!=G.arcnum;++i)
{
if(edgs[i].weight<m)
{
m=edgs[i].weight;
x=edgs[i].pre;
y=edgs[i].bak;
n=i;
}
}
// cout<<x<<y<<m;
// cout<<endl;
buf=find(acrvisited,x);
edf=find(acrvisited,y);
// cout<<buf<<" "<<edf<<endl;
edgs[n].weight=10000;
if(buf!=edf)
{
acrvisited[buf]=edf;
cout<<"("<<x<<","<<y<<")"<<m;
cout<<endl;
}
}
}
void main()
{
algraph gra;
MGraph_L G;
int i,d,g[20][20];
char a='a';
d=creatMGraph_L(G);
creatadj(gra,G);
vnode v;
cout<<endl<<"……####注意:若該圖為非強連通圖(含有多個連通分量)時"<<endl
<<" 最小生成樹不存在,則顯示為非法值。"<<endl<<endl;
cout<<"…………………菜單……………………"<<endl<<endl;
cout<<"0、顯示該圖的鄰接矩陣……………………"<<endl;
cout<<"1、顯示該圖的鄰接表……………………"<<endl;
cout<<"2、深度優先遍歷…………………………"<<endl;
cout<<"3、廣度優先遍歷…………………………"<<endl;
cout<<"4、最小生成樹PRIM演算法…………………"<<endl;
cout<<"5、最小生成樹KRUSCAL演算法………………"<<endl;
cout<<"6、該圖的連通分量………………………"<<endl<<endl;
int s;
char y='y';
while(y='y')
{
cout<<"請選擇菜單:"<<endl;
cin>>s;
switch(s)
{
case 0:
cout<<"鄰接矩陣顯示如下:"<<endl;
ljjzprint(G);
break;
case 1:
cout<<"鄰接表顯示如下:"<<endl;
adjprint(gra);
break;
case 2:
cout<<"廣度優先遍歷:";
bfstra(gra);
cout<<endl;
break;
case 3:
for(i=0;i!=gra.vexnum;++i)
{
visited[i]=0;
}
cout<<"深度優先遍歷:";
dfstra(gra);
cout<<endl;
break;
case 4:
for(i=0;i!=G.vexnum;++i)
for(int j=0;j!=G.vexnum;++j)
g[i+1][j+1]=G.arcs[i][j].adj;
cout<<"prim:"<<endl;
prim(g,d);
break;
case 5:
cout<<"kruscal:"<<endl;
kruscal_arc(G,gra);
break;
case 6:
cout<<"連通分量:";
bfstra_fen(gra);
break;
}
cout<<endl<<"是否繼續?y/n:";
cin>>y;
if(y=='n')
break;
}
}
另外,虛機團上產品團購,超級便宜
2. 求一個有向圖圖遍歷的演算法
static int countOfWays = 0;
int traverseForNum(vertex* sourceBuf, vertex* targetBuf){
int i = 0;
if (sourceBuf->searched){
return 1; /* already searched */
}else{ /* not searched yet */
sourceBuf->searched = 1;
for ( i = 0; i < sourceBuf->numOfGoing; i ++ ){
if(sourceBuf->goingVertex->[i] == targetBuf){
countOfWays ++;
}
}
for ( i = 0; i < sourceBuf->numOfGoing; i ++ ){
if(sourceBuf->goingVertex->[i] != targetBuf){
traverseForNum(sourceBuf->goingVertex[i]), targetBuf);
}
}
}
return 1;
}
如下是頂點的定義:
typedef struct buf vertex;
struct buf{
vertex **goingVertex; /* vertex out the current */
vertex **comingVertex; /* vertex into the current */
int numOfGoing; /* count of vertex going list */
int numOfComing; /* count of vertex coming list */
int searched; /* flag used to search for the graph */
};
代碼我以前調試過,完全OK。
但是你給我的這個題目,我是基於以前的代碼給你修改的,C99下即使調試不通過問題也不太大,你稍微修改一下就好。但是我有需要說明的:這個是做出你問的第一個問題的答案;第二個問題,我想可能跟乘法定律有關,可以用j到i的路徑數目乘以i到k的路徑數目,演算法我想了一下,如果非要用一個遍歷作出來,還真要花點兒時間,還不一定最優。
3. 圖的矩陣深度和廣度遍歷演算法
圖的遍歷是指從圖中任一給定頂點出發,依次訪問圖中的其餘頂點。如果給定的圖是連通圖,則從圖中的任意一點出發,按照一個指定的順序就可以訪問到圖中的所有頂點,且每個頂點只訪問一次。這個過程稱為圖的遍歷。
圖的遍歷比樹的遍歷復雜的多。樹是一種特殊類型的圖,即無圈(無迴路)連通圖。樹中的任意兩個頂點間都有唯一的路徑相通。在一個頂點被訪問過之後,不可能又沿著另外一條路徑訪問到已被訪問過的結點。而圖中的頂點可能有邊與其他任意頂點相連
。因此在訪問了某個頂點之後,可能沿著另一條邊訪問已被訪問過的頂點。例如圖(a)中的G1,在訪問了V1,V2和V3之後,有可能沿著邊(V3,V1)訪問到V1。為了避免一頂點被多次訪問,可以設立一個集合Visited,用來記錄已被訪問過的頂點。它的初值為空
集。一旦V1被訪問過,即把V1加到集合Visited中。圖的遍厲通常有兩種:圖的深度優先
搜索和圖的廣度優先搜索。
1)圖的深度優先搜索
從圖G=(V,E)的一個頂點V0出發,在訪問了任意一個與V0相鄰且未被訪問過的頂點W1之後,再從W1出發,訪問和W1相鄰且未被訪問過的頂點W2,然後再從W2出發進行如上所述訪問,直到找到一個它相鄰的結點,都被訪問過的結點為止。然後退回到尚有相
鄰結點未被訪問過的頂點,再從該頂點出發,重復上述搜索過程,直到所有被訪問過的頂點的鄰接點都被訪問過為止。圖的這種遍歷過程就稱為圖的深度優先搜索。例如從頂點V1出發對圖3.3.5進行深度優先搜索,遍歷的順序為 V1,V2,V5,V10,V6,V7,V3,V12,V1
1,V8,V4,V9。(與鄰接表中的鄰接點排列順序有關,即p->next.vertex=v2 or v3對遍歷
順序有影響 )
例25.(p194.c)圖的深度優先搜索。從圖G的頂點V0
發進行深度優先搜索,列印出各個頂點的遍歷順序。
解:圖的深度優先搜索法為:
(1)首先訪問V0並把V0加到集合visited中;
(2)找到與V0相鄰的頂點W,若W未進入
visited中,則以深度優先方法從W開始搜索。
(3)重復過程(2)直到所有於V0相鄰的頂點
都被訪問過為止。
下面是對用鄰接表表示的圖G進行深度優先搜索的程序
int rear=0; /*Visit和rear都為全局變數*/
int visit[500];
depth_first_search(g,v0) /*從V0開始對圖G進行深度
優先搜索*/
graphptr g[ ]; /*指針數組,為鄰接表表頭頂點指針
g[vi]...g[vn]*/
int v0; /*這里V0和W都是頂點標號,如V0=0或1*/
{ /*g[v0]是頂點V0的表頭指針*/
int w;
graphptr p; /*鏈表的結點指針*/
visit [++rear]=v0;
printf("%d\n",v0);
p=g[v0];/*指定一個頂點,通過鄰接表表頭指針
,訪問v0的鄰接頂點*/
while (p!=NULL)
{
w=p->vertex ;/*這里W是與V0相鄰的一個頂點*/
if (!visited(w))/*當V0的相鄰結點,W未被訪問時,從W開始遍厲*/
depth_first_search(g,w);
p=p->next;/*接著訪問另一個相鄰頂點*/
}
}
int visited(w) /*檢查頂點w是否進入visited(w)*/
int w ;
{
int i;
for (i=1;i<=rear;i++)
if (visit [ i ] == w) return(1);/*W在visit[]中,說明被訪問過*/
return(0); /*W不在visit[]中,說明未被訪問過,返回0*/
}
2)圖的廣度優先搜索
從圖G的一個頂點V0出發,依次訪問V0的鄰接點K1,K2...Kn。然後再順序訪問K1,K2...Kn的所有尚未被訪問過的鄰接點。如此重復,直到圖中的頂點都被訪問過為止。圖的這種搜索稱為圖的廣度優先搜索。例如:從V1出發按廣度優先搜索方法遍歷圖3.3.5,頂
點的訪問順序為V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12。
圖的廣度優先搜索類似樹的按層次遍歷,需要有一個隊列來存放還沒
有來得及處理的頂點。圖的廣度優先搜索演算法為:
(1)首先把V0放入隊列;
(2)若隊列為空則結束,否則取出隊列的頭V;
(3)訪問V並把所有與V相鄰且未被訪問的頂點插入隊列;
(4)重復(2)-(3)直到隊列為空。
上述演算法中所有已被訪問過的頂點都放在隊列中,因此只要檢查某個頂點是否在隊列中就可以判斷出該頂點是否已被訪問過。
廣度搜索法的程序如下:
broad_first_search(g,v0) /*從V0開始對圖g進行廣度優先搜索*/
graphptr g[ ]; /*為鄰接表,表頭頂點指針*/
int v0;
{
int queue[500],front =1, tail=1,v;
graphptr p;
queue [tail]=v0; /*把V0插入隊列queue*/
while (front <=tail)/*當隊列不為空*/
{
v=queue[front++]; /*取出隊列中的頂點*/
printf("%d\n",v); /*訪問該頂點*/
p=g[v]; /*從頂點V的鏈表來考慮與V相鄰的頂點*/
while (p!=NULL)
{
v=p->vertex; /*從第一個結點(即邊)中找出相鄰的頂點*/
if (!visited(queue,tail,v))/*判斷頂點是否進入隊列,如進入隊列
說明已被訪問或將要訪問*/
queue[++tail]=v;/*如果該頂點未被訪問過,將此相鄰頂點插入隊列*/
p=p-->next;/*再考慮該結點的下一個相鄰頂點*/
}
}
}
visited (q,tail,v)/*判斷頂點是否被訪問過,訪問過時,返回1,否則返回0*/
int q[ ],tail,v;/*進入隊列的頂點,在front之前的頂點已被訪問過列印輸出,
在front和tail之間的頂點是即將要訪問頂點*/
{
int i;
for(i=1;i<=tail;i++)/*掃描隊列,確定v是否在隊列中,在隊列中返回1,否則返回0*
/
if (q[i]==v)return(1);/*隊列中的頂點都認為已被訪問過*/
return(0);
}
深度優先的非遞歸演算法
/*設當前圖(或圖的某個連通分枝)的起始訪問點為p*/
NodeType stackMain,stackSec
visit(p)
p->mark=true;
do
{
for(all v isTheConnectNode of (G,p))//將當前點的鄰接點中的所有結點壓入副棧中
if(v.marked==false)
statckSec.push(v)
//將副棧中的點依次彈出,壓入主棧中,這與非遞歸演算法中使用隊列的意圖類似
while(!stackSec.isEmpty())
stackMain.push(statckSec.pop());
do//找出下一個未訪問的結點或者沒找到,直到棧為空
{
if(!stackMain.isEmpty())
{
p=stackMain.pop();
}
}while(p.marked==true&&!stackMain.isEmpty())
if(p.marked==false)//訪問未訪問結點.
{
visit(p);
p.marked=true;
}
}while(!stackMain.isEmpty())
4. C語言 圖的遍歷
思路:
以鄰接表或鄰接矩陣為存儲結構,實現連通無向圖的深度和廣度優先遍歷。以用戶指定的結點為起始點
,分別輸出每種遍歷下的結點訪問序列和相應的生成樹的邊集。
設圖的結點不超過30個,每個結點用一個編號表示。通過輸入圖的全部邊輸入一個圖,每個邊為一個數對
可以對邊的輸入順序作出某種限制。注意,生成樹和生成邊是有向邊,端點順序不能顛倒。
5. 圖的遍歷演算法java解決方案
二叉樹具有以下重要性質:
性質1 二叉樹第i層上的結點數目最多為2i-1(i≥1)。
證明:用數學歸納法證明:
歸納基礎:i=1時,有2i-1=20=1。因為第1層上只有一個根結點,所以命題成立。
歸納假設:假設對所有的j(1≤j<i)命題成立,即第j層上至多有2j-1個結點,證明j=i時命題亦成立。
歸納步驟:根據歸納假設,第i-1層上至多有2i-2個結點。由於二叉樹的每個結點至多有兩個孩子,故第i層上的結點數至多是第i-1層上的最大結點數的2倍。即j=i時,該層上至多有2×2i-2=2i-1個結點,故命題成立。
性質2 深度為k的二叉樹至多有2k-1個結點(k≥1)。
證明:在具有相同深度的二叉樹中,僅當每一層都含有最大結點數時,其樹中結點數最多。因此利用性質1可得,深度為k的二叉樹的結點數至多為:
20+21+…+2k-1=2k-1
故命題正確。
性質3 在任意-棵二叉樹中,若終端結點的個數為n0,度為2的結點數為n2,則no=n2+1。
證明:因為二叉樹中所有結點的度數均不大於2,所以結點總數(記為n)應等於0度結點數、1度結點(記為n1)和2度結點數之和:
n=no+n1+n2 (式子1)
另一方面,1度結點有一個孩子,2度結點有兩個孩子,故二叉樹中孩子結點總數是:
nl+2n2
樹中只有根結點不是任何結點的孩子,故二叉樹中的結點總數又可表示為:
n=n1+2n2+1 (式子2)
由式子1和式子2得到:
no=n2+1
滿二叉樹和完全二叉樹是二叉樹的兩種特殊情形。
1、滿二叉樹(FullBinaryTree)
一棵深度為k且有2k-1個結點的二又樹稱為滿二叉樹。
滿二叉樹的特點:
(1) 每一層上的結點數都達到最大值。即對給定的高度,它是具有最多結點數的二叉樹。
(2) 滿二叉樹中不存在度數為1的結點,每個分支結點均有兩棵高度相同的子樹,且樹葉都在最下一層上。
【例】圖(a)是一個深度為4的滿二叉樹。
2、完全二叉樹(Complete BinaryTree)
若一棵二叉樹至多隻有最下面的兩層上結點的度數可以小於2,並且最下一層上的結點都集中在該層最左邊的若干位置上,則此二叉樹稱為完全二叉樹。
特點:
(1) 滿二叉樹是完全二叉樹,完全二叉樹不一定是滿二叉樹。
(2) 在滿二叉樹的最下一層上,從最右邊開始連續刪去若干結點後得到的二叉樹仍然是一棵完全二叉樹。
(3) 在完全二叉樹中,若某個結點沒有左孩子,則它一定沒有右孩子,即該結點必是葉結點。
【例】如圖(c)中,結點F沒有左孩子而有右孩子L,故它不是一棵完全二叉樹。
【例】圖(b)是一棵完全二叉樹。
性質4 具有n個結點的完全二叉樹的深度為
證明:設所求完全二叉樹的深度為k。由完全二叉樹定義可得:
深度為k得完全二叉樹的前k-1層是深度為k-1的滿二叉樹,一共有2k-1-1個結點。
由於完全二叉樹深度為k,故第k層上還有若干個結點,因此該完全二叉樹的結點個數:
n>2k-1-1。
另一方面,由性質2可得:
n≤2k-1,
即:2k-1-l<n≤2k-1
由此可推出:2k-1≤n<2k,取對數後有:
k-1≤lgn<k
又因k-1和k是相鄰的兩個整數,故有
,
由此即得:
6. 連通圖的深度優先遍歷演算法
這個第一個點是隨機的。只是看你怎麼儲存的。如果你把v的鄰接頂點用數組保存,那麼它在數組的最前邊。用指針的話,就指向下一個緊接的位置。
7. 圖的圖的遍歷
常見的圖遍歷方式有兩種:深度優先遍歷和廣度優先遍歷,這兩種遍歷方式對有向圖和無向圖均適用。 深度優先遍歷的思想類似於樹的先序遍歷。其遍歷過程可以描述為:從圖中某個頂點v出發,訪問該頂點,然後依次從v的未被訪問的鄰接點出發繼續深度優先遍歷圖中的其餘頂點,直至圖中所有與v有路徑相通的頂點都被訪問完為止。
深度優先遍歷演算法實現:
為了便於在演算法中區分頂點是否已被訪問過,需要創建一個一維數組visited[0..n-1](n是圖中頂點的數目),用來設置訪問標志,其初始值visited(0≤i≤n-1)為"0",表示鄰接表中下標值為i的頂點沒有被訪問過,一旦該頂點被訪問,將visited置成"1"。
int visited[0..n-1]={0,0,...0};
void DFS(AdjList adj,int v)
{//v是遍歷起始點的在鄰接表中的下標值,其下標從0開始
visited[v]=1; visited(adj[v].elem);
for (w=adj[v].firstedge;w;w=w->next)
if (!visited[w->adjvex]) DFS(adj,w->adjvex);
}
對於無向圖,這個演算法可以遍歷到v頂點所在的連通分量中的所有頂點,而與v頂點不在一個連通分量中的所有頂點遍歷不到;而對於有向圖可以遍歷到起始頂點v能夠到達的所有頂點。若希望遍歷到圖中的所有頂點,就需要在上述深度優先遍歷演算法的基礎上,增加對每個頂點訪問狀態的檢測: intvisited[0..n-1]={0,0,...0};voidDFSTraverse(AdjListadj){for(v=0;v<n;v++)if(!visited[v])DFS(adj,v);} 對圖的廣度優先遍歷方法描述為:從圖中某個頂點v出發,在訪問該頂點v之後,依次訪問v的所有未被訪問過的鄰接點,然後再訪問每個鄰接點的鄰接點,且訪問順序應保持先被訪問的頂點其鄰接點也優先被訪問,直到圖中的所有頂點都被訪問為止。下面是對一個無向圖進行廣度優先遍歷的過程。
下面我們討論一下實現廣度優先遍歷演算法需要考慮的幾個問題:
(1)在廣度優先遍歷中,要求先被訪問的頂點其鄰接點也被優先訪問,因此,必須對每個頂點的訪問順序進行記錄,以便後面按此順序訪問各頂點的鄰接點。應利用一個隊列結構記錄頂點訪問順序,就可以利用隊列結構的操作特點,將訪問的每個頂點入隊,然後,再依次出隊,並訪問它們的鄰接點;
(2)在廣度優先遍歷過程中同深度優先遍歷一樣,為了避免重復訪問某個頂點,也需要創建一個一維數組visited[0..n-1](n是圖中頂點的數目),用來記錄每個頂點是否已經被訪問過。
int visited[0..n-1]={0,0,...0};
void BFS(AdjList adj,int v)
{//v是遍歷起始點在鄰接表中的下標,鄰接表中下標從0開始
InitQueue(Q); //Q是隊列
visited[v]=1; visite(adj[v].elem); EnQueue(Q,v);
while (!QueueEmpty(Q)) {
DeQueue(Q,v);
for (w=adj[v].firstedge;w;w=w->next)
if (!visited[w->adjvex]) {
visited[w->adjvex]=1;
visite(adj[w->adjvex].elem);
EnQueue(Q,w->adjvex); }
}
}
8. 用C語言編程實現圖的遍歷演算法
圖的遍歷是指按某條搜索路徑訪問圖中每個結點,使得每個結點均被訪問一次,而且僅被訪問一次。圖的遍歷有深度遍歷演算法和廣度遍歷演算法,最近阿傑做了關於圖的遍歷的演算法,下面是圖的遍歷深度優先的演算法(C語言程序):
#include<stdio.h>
#include<malloc.h>
#define MaxVertexNum 5
#define m 5
#define TRUE 1
#define NULL 0
typedef struct node
{
int adjvex;
struct node *next;
}JD;
typedef struct EdgeNode
{
int vexdata;
JD *firstarc;
}TD;
typedef struct
{
TD ag[m];
int n;
}ALGRAPH;
void DFS(ALGRAPH *G,int i)
{
JD *p;
int visited[80];
printf("visit vertex:%d->",G->ag[i].vexdata);
visited[i]=1;
p=G->ag[i].firstarc;
while(p)
{
if (!visited[p->adjvex])
DFS(G,p->adjvex);
p=p->next;
}
}
void creat(ALGRAPH *G)
{
int i,m1,j;
JD *p,*p1;
printf("please input the number of graph\n");
scanf("%d",&G->n);
for(i=0;i<G->n;i++)
{
printf("please input the info of node %d",i);
scanf("%d",&G->ag[i].vexdata);
printf("please input the number of arcs which adj to %d",i);
scanf("%d",&m1);
printf("please input the adjvex position of the first arc\n");
p=(JD *)malloc(sizeof(JD));
scanf("%d",&p->adjvex);
p->next=NULL;
G->ag[i].firstarc=p;
p1=p;
for(j=2 ;j<=m1;j++)
{
printf("please input the position of the next arc vexdata\n");
p=(JD *)malloc(sizeof(JD));
scanf("%d",&p->adjvex);
p->next=NULL;
p1->next=p;
p1=p;
}
}
}
int visited[MaxVertexNum];
void DFSTraverse(ALGRAPH *G)
{
int i;
for(i=0;i<G->n;i++)
visited[i]=0;
for(i=0;i<G->n;i++)
if(!visited[i])
DFS(G,i);
}
int main()
{
ALGRAPH *G;
printf("下面以臨接表存儲一個圖;\n");
creat(G);
printf("下面以深度優先遍歷該圖 \n");
DFSTraverse(G);
getchar();
}
9. 什麼叫遍歷演算法(最好有例子)
遍歷演算法:所謂遍歷(Traversal),是指沿著某條搜索路線,依次對樹中每個結點均做一次且僅做一次訪問。訪問結點所做的操作依賴於具體的應用問題。遍歷是二叉樹上最重要的運算之一,是二叉樹上進行其它運算之基礎。當然遍歷的概念也適合於多元素集合的情況,如數組。
遍歷演算法概念延伸:
圖遍歷:圖遍歷又稱圖的遍歷,屬於數據結構中的內容。指的是從圖中的任一頂點出發,對圖中的所有頂點訪問一次且只訪問一次。圖的遍歷操作和樹的遍歷操作功能相似。圖的遍歷是圖的一種基本操作,圖的許多其它操作都是建立在遍歷操作的基礎之上。
舉例:
遍歷二叉樹搜索路線:
從二叉樹的遞歸定義可知,一棵非空的二叉樹由根結點及左、右子樹這三個基本部分組成。因此,在任一給定結點上,可以按某種次序執行三個操作:⑴訪問結點本身(N),⑵遍歷該結點的左子樹(L),⑶遍歷該結點的右子樹(R)。以上三種操作有六種執行次序:NLR、LNR、LRN、NRL、RNL、RLN。前三種次序與後三種次序對稱。
遍歷二叉樹的執行蹤跡三種遞歸遍歷演算法的搜索路線相同(如下圖虛線所示)。具體線路為:從根結點出發,逆時針沿著二叉樹外緣移動,對每個結點均途徑三次,最後回到根結點。