趣谈数据结构十.docx

上传人:b****7 文档编号:23352855 上传时间:2023-05-16 格式:DOCX 页数:16 大小:46.41KB
下载 相关 举报
趣谈数据结构十.docx_第1页
第1页 / 共16页
趣谈数据结构十.docx_第2页
第2页 / 共16页
趣谈数据结构十.docx_第3页
第3页 / 共16页
趣谈数据结构十.docx_第4页
第4页 / 共16页
趣谈数据结构十.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

趣谈数据结构十.docx

《趣谈数据结构十.docx》由会员分享,可在线阅读,更多相关《趣谈数据结构十.docx(16页珍藏版)》请在冰豆网上搜索。

趣谈数据结构十.docx

趣谈数据结构十

趣谈数据结构(十)

福州一中陈颖

  本讲通过几个例子,着重讲解图的两种存储结构--邻接表和邻接矩阵的使用,介绍图应用中常见的几种问题的处理方法及算法。

  例1有如图1所示的七巧板,试编写一源程序如下,使用至多四种不同颜色对七巧板进行涂色(每块涂一种颜色),要求相邻区域的颜色互不相同,打印输出所有可能有

涂色方案。

  分析:

本题实际上就是著名的"地图四色"问题。

无论地图多么复杂,只需用四种颜色就可以将相邻的区域分开。

               

                   图1

  为了解题方便,我们可以把七巧板上每一个区域看成一个顶点,若两个区域相邻,则相应的顶点间用一条边相连,这样,该问题就转化为图的问题了。

  算法步骤:

  按顺序分别对1号、2号、......、7号区域进行试探性涂色,用1、2、3、4号代表4种颜色。

数据采用邻接矩阵。

  ⒈对某一区域涂上与其相邻区域不同的颜色。

  ⒉若使用4种颜色进行涂色均不能满足要求,则回溯一步,更改前一区域的颜色。

  ⒊转步骤1继续涂色,直到全部结束为止,输出。

  源程序如下:

  programfour-colors(input,output,fdat);

  const

  max=10;

  type

  gradat=array[1..max,1..max]ofbyte;

  var

  data:

gradat;

  n:

byte;

  color:

array[1..max]ofbyte;

  total:

integer;

  proceduregetdata;{输入数据}

  var

  name:

string[12];

  fdat:

text;

  i,j:

byte;

  begin

  write('usewhichfile?

');

  readln(name);

  assign(fdat,name);

  reset(fdat);

  read(fdat,n);

  fori:

=1tondo

  forj:

=1tondo

  read(fdat,data[i,j]);

  fori:

=1tondo

  begin

  forj:

=1tondowrite(data[i,j]:

5);

  writeln;

  end;

  writeln;

  end;

  functioncolorsame(s:

byte):

boolean;{判断相邻点是否同色}

  var

  i:

byte;

  begin

  colorsame:

=false;

  fori:

=1tos-1do

  if(data[i,s]=1)and(color[i]=color[s])thencolorsame:

=true;

  end;

  procedureprint;{输出}

  var

  i:

byte;

  begin

  fori:

=1tondowrite(color[i]:

2);

  inc(total);

  writeln('con:

',total);

  end;

  proceduretry(s:

byte);{递归搜索}

  var

  i:

byte;

  begin

  ifs>7thenprint

  else

  begin

  i:

=0;

  repeat

  inc(i);

  color[s]:

=i;

  ifnot(colorsame(s))thentry(s+1);

  untili=4

  end;

  end;

  begin{主源程序如下}

  getdata;

  total:

=0;

  try

(1);

  writeln('Total=',total);

  readln;

  end.

  fdat文件内容:

  7

  0100101

  1001010

  0000011

  0100011

  1000001

  0111000

  1011100

  例2对图2从V1点出发,沿着边系统地访问该图中其它所有的顶点一次且仅一次。

(用两种不同的解法)

   

         图2               图3

  分析:

从图上某点出发,沿边访问图中其它所有的顶点一次且仅一次,称为图的遍历。

图的遍历有两种方式:

深度优先搜索遍历与广度优先搜索遍历。

  算法步骤:

  

(一)深度优先搜索遍历算法

  ⒈以给定的某个顶点V0为起始点,访问该顶点;

  ⒉选取一个与顶点V0相邻接且未被访问过的顶点V1,用V1作为新的起始点,重复上述过程;

  ⒊当到达一个其所有邻接的顶点都已被访问过的顶点Vi时,就退回到新近被访问过的顶点Vi- 1,继续访问Vi-1尚未访问的邻接点,重复上述搜索过程;

  ⒋直到从任意一个已访问过的顶点出发,再也找不到未被访问过的顶点为止,遍历便告完成。

  这种搜索的次序体现了向纵深发展的趋势,所以称之为深度优先搜索。

  图13.14中从V1出发的一种深度优先搜索访问顺序:

  V1-->V2-->V4-->V8-->V5-->V6-->V3-->V7

  

(二)广度优先搜索遍历算法

  ⒈从图中的某一顶点V0开始,先访问V0;

  ⒉访问所有与V0相邻接的顶点V1,V2,......,Vt;

  ⒊依次访问与V1,V2,......,Vt相邻接的所有未曾访问过的顶点;

  ⒋循此以往,直至所有的顶点都被访问过为止。

  这种搜索的次序体现沿层次向横向扩长的趋势,所以称之为广度优先搜索。

  图13.14中,从V1出发的一种广度优先搜索访问顺序为:

  V1-->V2-->V3-->V4-->V5-->V6-->V7-->V8

  源程序如下:

  源程序如下中数据采用邻接表方式表示。

邻接表如图13.15所示。

  存储邻接表数据需要三个变量,一个存储结点的内容,一个存储结点指向下一个结点的指针,一个存储每个链表的链头指针。

设数组变量A(i,0)存放结点,A(i,1)存放结点的指针,H存放链头指针。

  数据存放形式如表1所示。

 

                 表1

   

  源程序如下:

programbianli(input,output,fdat);

const

max=10;

type

gradat=array[1..max,1..max]ofinteger;

way=setof1..max;

var

data:

gradat;

n,k:

byte;

path:

array[1..max]ofbyte;

already:

way;

m:

integer;

proceduregetdata;{输入数据}

var

name:

string[12];

fdat:

text;

i,j:

byte;

begin

write('usewhichfile?

');

readln(name);

assign(fdat,name);

reset(fdat);

read(fdat,n);

fori:

=1tondo

begin

j:

=0;

repeat

inc(j);

read(fdat,data[i,j]);

untildata[i,j]=0;

end;

fori:

=1tondo

begin

j:

=1;

write('[',i:

2,']');

whiledata[i,j]<>0do

begin

write(data[i,j]:

3);

inc(j);

end;

writeln;

end;

end;

procedureprint;{输出}

var

i:

byte;

begin

fori:

=1tondowrite(path[i]:

2);

writeln;

end;

procedurewidth(s:

byte);{广度优先搜索遍历}

var

useful,next:

way;

i,j:

byte;

begin

k:

=1;

path[k]:

=s;

useful:

=[s];

already:

=[s];

next:

=[];

repeat

fori:

=1tondo

ifiinusefulthen

begin

j:

=1;

repeat

ifnot(data[i,j]inalready)then

begin

next:

=next+[data[i,j]];

already:

=already+[data[i,j]];

inc(k);

path[k]:

=data[i,j];

end;

inc(j)

untildata[i,j]=0;

end;

useful:

=next;

next:

=[];

untilk=n;

end;

proceduredeepth(s:

byte);{深度优先搜索遍历}

var

i:

byte;

begin

i:

=1;

inc(k);

path[k]:

=s;

already:

=already+[s];

whiledata[s,i]<>0do

begin

ifnot(data[s,i]inalready)

thendeepth(data[s,i]);

inc(i);

end;

end;

begin

getdata;

k:

=0;

already:

=[];

repeat

write('Whichdoyouwanttobegin:

');

readln(m);

until(m>0)and(m<=n);

deepth(m);

write('Deeppathis:

');

print;

width(m);

write('Widepathis:

');

print;

readln;

end.

fdat文件数据:

8

230

1450

1670

280

280

380

380

45670

  例3如图4所示,从A到B有好几条可供选择的路线,每条路线的长度均标示在图上,问选择什么样的路线,可使从A到B所走的路径最短?

      

               图4

  分析:

在一个有向图G=(V,E)中,给定一个始点V1和终点Vp,对每条弧(Vi,Vj)相应地有一个权W(i,j),最短路问题就是要求从始点V1到终点Vp的一条路径,使其在所有从V1到Vp的路径中,它是总权最小的一条。

  当所有的W(i,j)>=0时,解决最短路径问题的比较好的方法是Dijkstra方法。

它是荷兰计算机科学家(E.W.Dijkstra)于1959年提出的。

这个方法不仅求出了从A到顶点B的最短路径,而且求出了从A到各顶点的最短路径。

  算法步骤:

  从V1开始,给每一个顶点赋予一个标号,标号分为两种:

  

(1)T标号--V1到该顶点的最短路径的上界(临时标号);

  

(2)P标号--V1到该顶点的最短路径(固定标号)。

  若某一顶点已得到P标号,则表明已求出V1到该点的最路径,凡未得到P标号的顶点,一律标上T标号。

  算法的每一步把某一顶点的T标号改变为P标号,最多经过(n-1)步(n为顶点个数),即可得到从V1到每一个顶点的最短路径。

  ⒈在初始状态下,令P(V1)=0,T(Vi)=+∞(i=1,2,3,......,n),反复执行下面两个步骤;

  ⒉设Vi是已经得到P标号的点,考虑所有满足下列条件的顶点Vi的集合B:

  B={Vj|(Vi,Vj)E且Vj是T标号}

将集合B中的每一个顶点Vj的T标号修改为:

min{T(Vj),P(Vi)+W(i,j)}

  ⒊若G中已没有T标号的顶点,则结束,否则,将集合B中最小者的T标号改变为P标号,然后转到2。

  下面,以求V1到V7的最短路径为例,说明Dijkstra方法:

开始,令P(V1)=0,T(Vj)=+∞(j=1,2,3,...,7)

  第一步:

考虑所有与V1相联且标有T标号的点的集合:

   B={V2,V3,V4};修改集合B中元素的T标号

   T(V2)=min{T(V2),P(V1)+W(1,2)}=min{+∞,2}=2

   T(V3)=min{T(V3),P(V1)+W(1,3)}=min{+∞,5}=5

   T(V4)=min{T(V4),P(V1)+W(1,4)}=min{+∞,3}=3

  将其中最小者改为标号:

P

(2)=2

  第二步:

考虑所有与V1,V2相联且标有T标号的点集合:

   B={V3,V4,V6};修改集合B中元素的T标号

   T(V3)=min{T(V3),P

(2)+W(2,3)}=min{5,4}=4

   T(V4)=3(同前面求法)

   T(V6)=min{T(V6),P

(2)+W(2,6)}=min{+∞,9}=9

  将其中最小者改变为P标号,P(V4)=3

  第三步:

考虑所有与V1,V2,V4相联且标有T标号的点的集合:

   B={V3,V5,V6};修改集合B中元素的T标号

   T(V3)=4

   T(V5)=min{T(V5),P(V4)+W(4,5)}=min{+∞,8}=8

   T(V6)=9

  将其中最小者改变为P标号:

P(V3)=4

  第四步:

考虑所有与V1,V2,V3,V4相联且标有T标号的点的集合:

   B={V5,V6};修改集合B中的元素的T标号

   T(V5)=min{T(V5),P(V3)+W(3,5)}=min{8,7}=7

   T(V6)=min{T(V6),P(V3)+W(3,6)}=min{9,9}=9

  将其中最小者改变为P标号:

P(V5)=7

  第五步:

考虑所有与V1,V2,V3,V4,V5相联且标有T标号的点的集合:

   B={V6,V7};修改集合B中的元素的T标号

   T(V6)=min{T(V6),P(V5)+W(5,6)}=min{9,8}=8

   T(V7)=min{T(V7),P(V5)+W(5,7)}=min{+∞,14}=14

  将其中最小者改变为T标号:

P(V6)=8

  第六步:

考虑所有与V1,V2,V3,V4,V5,V6相联且标有T标号的点的集合:

   B={V7};修改集合中B元素的T标号

   T(V7)=min{T(V7),P(V6)+W(6,7)}=min{14,13}=13

  将其中最小者改变为P标号:

P(V7)=13

  至此,所有顶点的T标号均已改变为P标号了,说明V1到各顶点的最短路径已全部找到。

  源程序如下:

programleastway;

const

max=10;

type

gradat=array[1..max,1..max]ofreal;

way=setof1..max;

var

a,b:

byte;

n:

byte;

data:

gradat;

path,path1:

array[1..max]ofbyte;

least:

real;

k:

byte;

proceduregetdata;{输入数据}

var

name:

string[12];

fdat:

text;

i,j:

byte;

begin

write('usewhichfile?

');

readln(name);

assign(fdat,name);

reset(fdat);

read(fdat,n);

fori:

=1tondo

forj:

=1tondo

read(fdat,data[i,j]);

fori:

=1tondo

begin

forj:

=1tondowrite(data[i,j]:

5);

writeln;

end;

writeln;

end;

procedureprint;{输出}

var

i:

byte;

begin

i:

=1;

write('Pathis:

',a:

2);

whilepath[i]<>0do

begin

write(path[i]:

2);

inc(i);

end;

writeln;

writeln('Longth=',least);

end;

procedurelookfor(d:

integer;s:

real;k:

integer;already:

way);{求最短路径}

var

j:

byte;

begin;

forj:

=1tondo

if(data[d,j]<>0)andnot(jinalready)then

begin

path1[k]:

=j;

if(j=b)and(s+data[d,j]

begin

path:

=path1;

least:

=s+data[d,j];

path[k+1]:

=0;

end

elselookfor(j,s+data[d,j],k+1,already+[j]);

end;

end;

begin

getdata;

write('Fromwhich>');

readln(a);

write('Towhich>');

readln(b);

least:

=1e+36;

lookfor(a,0,1,[a]);

print;

readln;

end.

fdat文件数据:

7

0253000

0020070

0001350

0000500

0000017

0000005

0000000

 例4图5所示的网络代表6个城市间的交通网,边上权是公路的造价,现在要用公路把6个城市联系起来,这要修筑5条公路,如何设计使得这5条公路总的造价最小。

        

            图5

  分析:

这是一个最小生成树问题,所谓最小生成树就是从某个顶点出发遍历图中的各点,且使各边权的总和为最小,一个网络的最小生成树不一定是唯一的。

图6是图5的两个最小生成树,它们权的总和均为5+16+11+6+18=56。

    

   

                  图6

  算法步骤:

  构成最小生成树的典型算法有Prime算法与Kruskal算法等。

本题解答用Kruskal算法:

  ⒈先把平面上各顶点之间的边统统擦掉,只剩下一些孤立的点;

  ⒉把各边的长度按从小到大的次序排列好;

  ⒊按从小到大的次序把每边加到图中去,每加进去一条边,就判断一下是否产生回路?

没有回路就加进去下一条边;若产生回路就放弃这条边,而考虑下一条边,碰到两条等长的边,可任取一条;

  ⒋重复步骤3,直至加满(n-1)条边,就把n个顶点连接起来了,且边长总和为最小。

  源程序如下:

programleastcost;

const

max=10;

type

way=setof1..max;

gradat=array[1..max,1..max]ofreal;

var

data:

gradat;

n:

byte;

cost:

real;

already:

way;

k:

byte;

path:

array[1..2,1..max]ofbyte;

proceduregetdata;{输入数据}

var

name:

string[12];

fdat:

text;

i,j:

byte;

begin

write('usewhichfile?

');

readln(name);

assign(fdat,name);

reset(fdat);

read(fdat,n);

fori:

=1tondo

forj:

=1tondo

read(fdat,data[i,j]);

fori:

=1tondo

begin

forj:

=1tondowrite(data[i,j]:

5);

writeln;

end;

writeln;

end;

procedureprint;{输出}

var

i:

byte;

begin

fori:

=1ton-1do

writeln('Roadside:

',path[1,i]:

3,path[2,i]:

3);

writeln('Total=',cost);

end;

procedurelookfor(varn1,n2:

byte);{查找这一轮中的最小边}

var

i,j:

byte;

m:

real;

begin

m:

=1e+36;

fori:

=1tondo

ifiinalreadythen

forj:

=1tondo

if(not(jinalready))and(data[i,j]<>0)and(data[i,j]

begin

m:

=data[i,j];

n1:

=i;

n2:

=j;

end;

end;

procedureadd;{记录最小生成树及总造价}

var

i,j:

byte;

begin

repeat

lookfor(i,j);

already:

=already+[j];

cost:

=cost+data[i,j];

inc(k);

path[1,k]:

=i;

path[2,k]:

=j;

untilalready=[1..n];

end;

begin

getdata;

already:

=[1];

k:

=0;

cost:

=0;

add;

print;

readln;

end.

  fdat文件数据:

6

016001921

16056011

050600

06601814

190018033

2111014330

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 营销活动策划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1