2014考研计算机数据结构专项精讲课程讲义_第1页
2014考研计算机数据结构专项精讲课程讲义_第2页
2014考研计算机数据结构专项精讲课程讲义_第3页
2014考研计算机数据结构专项精讲课程讲义_第4页
2014考研计算机数据结构专项精讲课程讲义_第5页
已阅读5页,还剩86页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

绪 基本概 习 第一章线性 习 第二章栈、队列和数 队 数 习 第三章树与二叉 树的概 二叉 二叉树的.......................................................................................................... 树和森 习 第四章 图的概 邻接 图的遍 习 第五章查 散列 习 第六章排 插入排 冒泡排 排 快速排 堆排 基数排 习

=(D,R针反映数据元素间的逻辑关系。这种方式不要求空间连续,便于动态操作(如插入、删,索引表中索引指示结点的位置(下标)或区间端点(下标。间内,并将散列函数的值解释成关键字所在元素的地址。其特点是存取速度快,只能按T(n)n(输入量的多少,称之为问题规模)的某个函数T(n)=)增大,算法执行时间T(n)的增长率和f(n)的增长率相同。常见的渐进时间复杂度有:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<Ο(2n)<O(n!)<O(nn)间复杂度为O(1)。 voidfn){intx=1;while} B. C. D.2、以下算法的时间复杂度是 voidf(intintp=1,d=n,f=n;while(d>0){if(d%2==1) f=f*f;d=d/2;}} B. C. D.whileifp=p*f语句可以不考虑,因为它执行的次数不超过d=d/2语句的执行次数。f=f*f2T(n)≤n,即T(n)≤log2n=O(log2n)。比较同一简单类型的两个数据x1和x2的大小,对于x1>x2,x1=x2和x1<x2这三种不同情 charcompare(SimpleTypex1,SimpleTypex2){if(x1>x2)elseif(x1==x2)return‘=’;elsereturn‘<’;}voidReverse(char{intfor(inti=0;i<n/2;{charch;}}voidFind(inta[M][N],intm,intn,int&Lin,int {//MN for(intfor(intif}}voidfunc(int inty=while(y*yy}

其中nn=0需要说明的是:ai为序号为 线性表的顺序是指在内存中用地址连续的一块空间顺序存放线性表的各元素,用这种形式的线性表称其为顺序表。因为内存中的地址空间是线性的,因此,用物 #defineLIST_INIT_SIZE100 #defineLISTINCREMENT10 intInit_SqList(SqListL.elemElemType*malloc(LIST_INIT_SIZE*sizeof(ElemType));if(!L.elem)exit(OVERFLOW); L.listsize= return}插入后使原表长为n的表:(a1,a2,...,ai-1,ai,ai+1,...,an)成为表长为n+1表:(a1,a2,...,ai-1,x,ai,ai+1,...,an)。ai~an顺序向下移动,为新元素让出位置;(注意数据的移动方向:从后往前依次x置入空出的第iintInsert_SqList(SqList&L,inti,ElemTypeifi1||iL.length+1)return //if(L.length>=L.listsize)returnOVERFLOW;//当前空间已满,不能插q&(L.elem[i- qfor(p=&(L.elem[L.length-1]);p>=q;--*(p+1 //*q //插入 //表长增return}i(i1≤i≤n)个元素从线性表中去掉,删除后使原表长为n的线性表:(a1,a2,,ai-1,ai,ai+1,...,an)成为表长为n-1的线性表:(a1,a2,...,ai-1ai+1,...,an)。①将intDelete_SqList(SqList&L;inti)ifi1)||i return //p&(L.elem[i- pe //被删除元素的值赋给qL.elem+L.length- //for(++p;p<=q;*(p-1)= //被删除元后的元素左 //表长减return}素时,其后面的元素ai+1~an都要向上移动一个位置,共移动了n-i个元素,链表是通过一组任意的单元来线性表中的数据元素的。为建立起数据元间的线性关系,对每个数据元素ai,除了存放数据元素的自身的信息ai之外,还需要和ai一起存放其后继ai+1所在的单元的地址,这两部分信息组成一个“结点”,结点的结构如图所typedefstruct //struct //LinkList L的地址放在了指针变量L、H中,头指针为“NULL”则表示一个空表。链表与顺序表不同,它是一种动态管理的结构,链表中的每个结点占用的空间 (){LinkListL=NULL;LNodeintx; while(x!=flag) }return}致,则入方法。为每次将结点插入链表的部,所需加入一个指针rrr指向新结点(注意 Crea(){LinkListL=NULL; intx; while(x!=flag) //r}if return} “LL“”空表和” istR2(){LinkListL=(LNode*)malloc(sizeof(LNode)); intx; while(x!=flag) //r}returnL;} * L, i); while(p->next!=NULL&&j<i){p=p->next;j++;}if(j==i)returnp;}qq①p×②while(q- R来标识,可以使得操作效率其后继结点的指针则为p->next,而找其前驱则只能从该链表的头指针开始,顺着各结点的nextO(1)O(n),如果也希望找前驱的时间性能达到O(1),则只能付出空间的代价:每个结点再加一个指向前驱的指针域,typedefElemTypedata;structDuLNodep ②①④③sp指向双向链表中某结点,sp ②①④③s①s->prior=p-②p->prior-③s-④p-否则*p的前驱结点的指针就丢掉了。 ① O(n)(位置1、循环链表的主要优点是(单链 用()方式最节省运算时间。单链 B.仅有头指针的单循环链 D.仅有尾指针的单循环链q->prior=p;();7A=(a1,a2,am),B=(b1,b2,bn)均为顺序表,试编写一个比较A¸Bintcompare(SqListLa,SqListLb){while(i<La.Length&&{if(La.elem[i]==Lb.elem[i])i++;elseif(La.elem[i]<Lb.elem[i])return-elsereturn}if(i>La.length&&i>Lb.Length) if(i>Lb.Length) return1; return-}mink且小于maxkvoiddelete(LinkList&L,intmink,intwhile(p&&p- if(p)while(p&&p- //≥maxk //whiles=q delete q=s;}//}//}//voidinverse(LinkList&L)//逆置结点的单链表Lp=L->next;L->next=NULL;while(p){ p=}}voidunion(LinkList&Lc,LinkList&La,LinkList&Lb)//La和Lb归并为非递增的有序表Lc,归并之后,La和Lb//上述三个表均为结点的单链表,Lc表中的结点即为原La或Lb表中的结点Lc=new Lc->next=paLa pbLb //while(pa||pb) (!pa) {q=pb; pb=pb->next;}elseif (!pb) {q=pa; pa=pa->next;} if(pa->data<=pb->data){q= pa=pa->next; {q= pb=pb->next;q->nextLc Lc->next //}delete delete //释放LaLb}//voidDifference_L(LinkList&La,LinkListLb,LinkListLc){LaLbLcLaLcpre=pa; while(pa&&pb&&pc){if(pa->data<pb-{pre=pa; pa=pa->next;}elseif(pb->data<pc->data)else deletepa;pa=pre-}//pbpc}//p=L->next;//L为双向链表的头指针while(p!=L&&p->data!=x) p=p->next;//搜索元素值为x的结点*pif(p==L) returnNULL;while(q!=L&&q->freq<p->freq) q=q->prior;//搜索频度不小于它的结点*q将结点*p从当前位置上删除;*p*q之后;returnp;voidOneToThree(LinkedList&L,LinkedList&la,LinkedList&ld,LinkedList&{la=(LinkedList)malloc(sizeof(LNode)); r=L;L=L {r->next=la->next;la elseif(r->data>=‘0’&&r- {}}}栈#defineSTACK_INIT_SIZE100 #defineSTACKINCREMENT10 需要注意,在栈的动态分配顺序结构中,base始终指向栈底元素,非空栈中的eintPush(SqStack&S,SElemType{if(S.top- *S.top return}SeOK,否则返回intPop(SqStack&S,SElemType ERROR;e=*--S.top;return}N=(Ndivd)×d+Nmodd (其中:div为整除运算,mod为求余运算)例如:(1348)10=(2504)8,其运算过程如下: Ndiv Nmod 出与其等值的八进制数。由于上述计算过程是从低位到顺序产生八进制数的各个数位,而打印输出,一般来说应从到低位进行,恰好和计算过程相反。因此,若将计算过程中N≠0Nrs(2);N=0s的内容依次出栈,NrN。voidconversion(){ //while(N){Push(S,N%8);N=N/8;}{printf("%d",e}}、*、/、%、^(乘方)和括号(。 +、 表达式作为一个满足表达式语则的串,如表达式“3*2^(4+*2-1*3)-”,它的3*2s1233#*3233#*32^(4+2*2-1*3)(-#5#96-5,91图中缀表达式3*2^(4+2*2-1*3)-5严格从左向右进行,而不用再考虑运算规则和级别。中缀表达式“3*2^(4+2*2-1*3)-5”的后 sch=*A++;InitStack(s);while(ch!=’#’){ Push(s,ch);else{Pops&a Pops&b)switchcasech==’+’: c=a+b;break;casech==’-’: c=a-b;break;casech==’*’:c=a*b;break;casech==’/’:c=a/b;break;casech==’%’:c=a%b;break;}Push(s,c)}ch=*A++}Pop(s,result); result;} 组A中,转换后的后缀表达式在字符数组B中。具体做法:遇到运算对象顺序向 运算符出栈后不是进行相应的运算,而是将其送入B中存放。具体算法在此不再赘述。在高级语言编制的程序中,调用函数与被调用函数之间的和信息交换必须通过栈进Acaoncr,这“递归工作栈”入的一端叫队尾(rear),把允许删除的一端叫队头(front)。#defineMAXQSIZE typedefstruct{ }intEnQueue(SqQueue&Q,QElemType{if((Q.rear+1)%MAXQSIZE==Q.front)returnERROR;Q.base[Q.rear]=e;Q.rear=(Q.rear+1)%return}{if(Q.front==Q.rear)returnERROR;e=Q.base[Q.front];Q.front=(Q.front+1)%MAXQSIZE;returnOK;}intQueueLength(SqQueue}typedefstructQNode{//QElemTypedata;structQNode}QNode,typedefstruct{ QueuePtrfront队头指针QueuePtrrear;//队尾指针} {QNode*)malloc(sieof(QNode);p->data=e;p->next=Q.rear->next=p;Q.rear=p;returnOK;}intDeQueue(LinkQueue&Q,QElemType&e)if(Q.front==Q.rear) p=Q.front->next;ep Q.rear=Q.front; return}一个数据元素有唯一的一组下标来标识,因此,在数组上不能做插入、删除数据元素的操作。现在来讨论数组在计算机中的表示。通常,数组在内存被映象为向量,即用向量作为数组的一种结构,这是因为内存的地址空间是一维的,数组的行列固定后,通过一个对于一维数组按下标顺序分配即可。对数组分配时,要把它的元素映象在一维器中,一般有两种方式:一是以行为主序(或先行后列)的顺序存放,即一行分配元,那么aij的物理地址可用一线性寻址函数计算:LOC(aij)=LOC(a11)+((i-1)*n+j-1)*LOC(aij)=LOC(a00)+(i*n+j)*d推广到一般的二维数组:A[c1..d1c2..d2],则aijLOC(aij)=LOC(ac1c2)+((i-c1)*(d2-c2+1)+(j-c2)可以对其元素进行随机存取,各种矩阵运算也非常简单,并且的密度为1。但是在矩阵阵,如三角矩阵、对称矩阵、对角矩阵、稀疏矩阵等,从节约空间的角度考虑,这种存储是不太合适的,看起来密度仍为1,但实际上占用了许多单元去重复的非零元素或零元素,这对高阶矩阵会造成极大的浪费,为了节省空间,我们可以对这类矩阵进行压缩:即为多个相同的非零元素只分配一个空间;对零元素不分配空间。线对称,因此只需上三角或下三角部分即可,比如,我们只下三角中的元素aij,其特点是中j≤i且1≤i≤n,对于上三角中的元素aij,它和对应的aji相等,因此当的元素在上三角时,直接去和它对应的下三角元素即可,这样,原来需要n*n个单元,现在只需要n(n+1)/2个单元了,节约了n(n-1)/2个单元。对下三角部分以行为主序顺序到一个向量中去,在下三角中共有n*(n+1)/2个元素,角中的某一个元素aij则具体对应一个sak,下面的问题是要找到ki、j之间的关系。 n(n+1)/2-……a1

第2 第3 第ni-11+2+…+i-1=i*(i-1)/2aijj个,所以在上面的排列顺序中,aiji*(i-1)/2+j个元素,因此它在SA中的下标ki、j的关系为: 若i<j,则aij是上三角中的元素,因为aij=aji,这样,上三角中的元素aij时则去访ajiSA

与对称矩阵类似,不同之处在于存完下三角中的元后,紧接着对角线上方的常SA[n*(n+1)+1]中,这种的方式可节约n*(n-1)-1个单元,sak与aji的对应关系为: ……ac1

项第2 第3 第n 项

对于上三角矩阵,思想与下三角类似,以行为主序顺序上三角部分,最后 (np)

元素,而 综上,sakaji的对应关系为: ……a…c第1 第2

第n行常为零(或同一个常数c)。sakaji00000 000000 图对角矩阵及压缩1、若循环队列以数组Q[0..m-1]作为其结构,变量rear表示循环队列中的队尾元素的实rear=(rear+1Modmlength表示当前循环队列中的元素个数,则循环队列的队首元素的实际位置是().A.rear-B.(rear-length+m)ModmC.(1+rear+m-length)ModmA. B. C. D.intsymmetry(charCh[])//若Ch[]为称字符序列,则返回1,否则返回0。p=Ch;InitStack(S);while(*p!=‘&’){Push(S,*p);p++;}state=1;p++;//滤去字符‘&’while(*p!=‘@’&&state){{Pop(S,e);p++;elsestate=}Statusex() {//若从终端依次输入的字符序列“回文//则返回TRUE,否则返回FALSE while(ch!=@){Push(S,ch); EnQueue(Q,ch);}{{Pop(S); elsestate=FALSE;}return}voiddemo1(sqstack&s){inti;arr[64];n=0;for(i=0;<n;i++)push(s,arr[i]);}voiddemo2(sqstack&s,intm){sqstackt;inti;while(!stackempty(s))while(!{i=pop(t);}}i=66,j=65A. B. C. D.将元素A[k](0≦k﹤m*n)表示成矩阵的第i行、第j列的元素(0≦i﹤m,0≦j﹤n)。A.i=k/n, B.i=k/m,C.i=k/n, D.i=k/m,树T中:n>1m(m>0)T1,0的结点称为分支结点,或者称为非终端结点。一棵树的结点父结点(1≤i<k),就把n1,n2,…,nk称为一条由n1nk的路径。这条路径的长度是k-1。先,而N称为M的子孙。1kn个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行性质 性质 x(1≤i≤kk x≤ ∑i

性质 (1-3- 2性质 2k、结点个数为n时,有 5n个结点的完全二叉树,如果按照从上至下和从左到右的顺序对二叉树中的所有结点从1开始顺序编号,则对于任意的序号为i的结点,有:序号为i的结点无右孩子。此外,若对二叉树的根结点从0开始编号,则相应的i所谓二叉树的顺序,就是用一组连续的单元存放二叉树中的结点。一般是按照二叉树结点从上至下、从左到右的顺序。这样结点在位置上的前驱后继关系并不一依据二叉树的性质,完全二叉树和满二叉树采用顺序比较合适,树中结点的序号可以唯一地反映出结点之间的逻辑关系,这样既能够最大可能地节省空间,又可以利用数对于一般的二叉树,如果仍按从上至下和从左到右的顺序将树中的结点顺序在一维这种对于需增加许多空结点才能将一棵二叉树改造成为一棵完全二叉树的时,会造成空间的大量浪费,不宜用顺序结构。的情况是右单支树,一棵深度为k的右单支树,只有k个结点,却需分配2k-1个单元。#defineMAX_TREE_SIZE100 typedefemTypeSqBiTree[MAX_TREE_SIZE]//0号单元存放根结点SqBiTree 左孩子和右孩子所在的链结点的地址。结点的的结构为: ypedata;structBiTNode 其中,data、lchildrchild三个域的意义与二叉链表结构相同;parent域为指向该结点双亲结点的指针。这种结构既便于查找孩子结点,又便于查找双亲结点;但是,相对因此,二叉链表是最常用的二叉树方式,本书后面所涉及到的二叉树的链式结构不{if }}{if //中序递归遍历b的左子树 //结点的数据域 //中序递归遍历b的右子树}}{ifPostOrder(b->lchild);//后序递归遍历b的左子树PostOrder(b->rchild);//后序递归遍历b的右子树 }}voidLevelOrder(BiTree{BiTreeQueue[MAX_TREE_SIZE];intwhile(front!=rear){ if(Queue[front]->lchild!=NULL) Queue[++rear]=Queue[front]->lchild;if(Queue[front]->rchild!=NULL) Queue[++rear]=Queue[front]->rchild;}}问,中序遍历是在从左子树返回时(第二次经过)遇到结点,后序遍历是在从右子树返 inttop=0;{while(p!=NULL) else{} }if(top<=0)return; else{ //指针指向p}}}只需将先序遍历的非递归算法中的Visit(p->data)移到p=Stacktop]和p=p->rchild叉树中每个结点在这个序列中的直接前驱结点和直接后继结点是什么,二叉树的结构中n个结点的二叉树中的叶子结点和一个具有n个结点的二叉树若采用二叉链表结构,在2n个指针域中只有n-1个指用结点空的右指针域(rchild)该结点在某种遍历序列中的直接后继结点的地址;对通常为每个结点增设两个标志位域ltag和rtag,令: { {rtag= typedefstructBiThrNode{ structBiThrNode }BiThrNode,为了将二叉树中所有空指针域都利用上,以及操作便利的需要,在线索二叉树时往实质上就是遍历一棵二叉树。在遍历过程中,Visit操作是检查当前结点的左、右指针域if(!(head=(BiThrNode*)malloc(sizeof(BiThrNode)))) if(!T)head->lchild=head; pre=head; }return}p){if(p){ if(!p->lchild){ p-}if(!pre->rchild){ } }}②如果该结点的左标志为0,表明该结点有左孩子,根据中序遍历的定义,它的前驱结点是以该结点的左孩子为根结点的的最右结点,即沿着其左的右指针链向下查找,当某结点的右标志为1时,它就是所要找的前驱结点。{BiThrTreepre;if(p-}②如果该结点的右标志为0,表明该结点有右孩子,根据中序遍历的定义,它的前驱结点是以该结点的右孩子为根结点的的最左结点,即沿着其右的左指针链向下查找,当某结点的左标志为1时,它就是所要找的后继结点。{BiThrTreepost;if(p-}以上给出的仅是在中序线索二叉树中寻找某结点的前驱结点和后继结点的算法。序在计算机中,树的有多种方式,既可以采用顺序结构,也可以采用链式结构,但无论采用何种方式,都要求结构不但能各结点本身的数据信息,还要能一组连续的空间(一维数组)树中的各个结点,数组中的一个元素表示树中的一个#define 100//typedefstruct #defineMAXNODE100 typedefstructChildNode{intstructChildNode}typedefstruct structChildNode emTypedata;structTreeNode*son;structTreeNode 这样,对树的操作实现就可以借助二叉树,利用二叉树上的操作来实现。(详见PPT)1.树的基本概n个带权值的叶结点,那么 WPL=∑W k和不同的带权路径长度,那么如何找到带权路径长度最小的二叉树(即树)呢?根据树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶结点越靠近根结点,n个权值{W1,W2,…,Wn}n棵只有一个叶结点的二叉树,从而得到一个二叉树的集合F={T1,T2,…,Tn};在F中选取根结点的权值最小和次小的两棵二叉树作为左、右构造一棵新的二叉树,在集合F中删除作为左、右的两棵二叉树,并将新建立的二叉树加入到集合F中(2(3)2.编和,也就是电文的代码总长,所以采用树构造的编码是一种能使电文代码总长最短的然而,采用树进行编码,则不会产生上述二义性问题。因为,在树中,每求编码,实质上就是在已建立的树中,从叶结点开始,沿结点的双亲链域回退到根结点,每回退一步,就走过了树的一个分支,从而得到一位码值,由于一个字符的编码是从根结点到相应叶结点所经过的路径上各分支所组成的0,1序 A. C. D.后序遍历序列中x在y之后,则x和y的关系是(。 B.x是y的右兄 D.x是y的后 A.n在m的右 B.n是m的祖C.n在m的左 D.n是m的子 a:b:c:d:e:f:g:=== node*fch,*nsib;intLeaves(Tree if(t=null)return }VRADTGraph数据关系R:VR={<v,w>|v,w∈VP(v,w),<v,w>v到w的弧,谓词P(v,w)定义了弧<v,w>的意义或信息}无向图:在一个图中,如果任意两个顶点构成的偶对(v,w)∈E是无序的,即顶有向图:在一个图中,如果任意两个顶点构成的偶对(v,w)∈E是有序的,即顶为无向完全图。在一个含有n个顶点的无向完全图中,有n(n-1)/2条边。连接,则称该图为有向完全图。在一个含有n个顶点的有向完全图中,有n(n-1)条边。(数目,记为ID(v)vv为始点的弧的数目,记为OD(v)。TDv)=IDv)+ODv)n(weight(networkvim,vq(vp,vi1,(vi1,v2),…,(vim,.vq)分别为图中的边。(VE’=(V’E’集,则称图G’是G的一个子图。vivj是连通的。如果图中任意两顶点都是连通的,则称该图是连通图。无向图的vivj(i≠j)均有GGn个顶点的一个极小连通Gn-1条边。在生成树中添加任意一条属于原图中的边必定会系为一个n×n的矩阵,矩阵的元素为:

{ 若(v,v或<v,v不是E(G){i i

若(vi,vj)或<vi,vj>是E(G)中的边0或∞若(vv或<v,vE(G)中的边{i i{其中,wij表示边(vi,vj)或<vi,vj>上的权值;∞表示一个计算机允许的、大于所有边上权值i行(i列)非零元素(或非∞元素)的个数正好是第i个顶点的度TD(vi)。i行(i列)非零元素(或非∞元素)的个数正好i个顶点的出度OD(vi)(或入度ID(vi)。 在用邻接矩阵图时,除了用一个二维数组用于表示顶点间相邻关系的邻接矩阵外,还需用一个一维数组来顶点信息,另外还有图的顶点数和边数。故可将其形式描述#defineMAX_VERTEX_NUM20 //最大顶点数设为20typedefcharVertexType; typedefintVRType; typedefstruct{ VRTypeedges[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵,即边表 邻接表(AdjacencyList)是图的一种顺序与链式结合的方法。邻接表表示法vi的邻接表,再将所有点的邻接表表头放到数组中,就 边(info#define ode typedefstruct typedef //ALGraph是以邻接表方式的图类型n个顶点、en2e个表结点。显整个邻接表。在所有链表中其邻接点域的值为i的结点的个数是顶点vi的入度。即对每个顶点vi建立一个以vi为头的弧的链表。O(n+e·e顶点(vi和vj)ij个链表,因此,不及邻接矩阵图的遍历是指从图中的任一顶点出发,对图中的所有顶点一次且只一次。图的此顶点,然后依次从v的未被的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被到;若此时图中尚有顶点未被,则另选图中一个未曾被的显然,这是一个递归的过程。为了在遍历过程中便于区分顶点是否已被,需附设访问标志数组visited[0:n-1],,其初值为FALSE,一旦某个顶点被,则其相应的分量置为voidDFSTraverse(GraphG){ for(v=0;v<G.vexnum;++v)visited[v]= for(v=0;v<G.vexnum; }voidDFS(GraphG,intv){ //从第v个顶点出发递归地深度优先遍历图G //第v个顶点if(!visited[w]) }找其邻接点的过程。其耗费的时间则取决于所采用的结构。当用二维数组表示邻接矩阵图的结构时,查找每个顶点的邻接点所需时间为O(n2),其中n为图中顶点数。而当以邻接表作图的结构时,找邻接点所需时间为O(e),其中e为无向图中边的数或有向图中弧的数。由此,当以邻接表作结构时,深度优先搜索遍历图的时间复杂度为O(n+e)。假设从图中某顶点v出发,在了v之后依次v的各个未曾过和邻接点,然后分别从这些邻接点出发依次它们的邻接点,并使“先被的顶点的邻接点”先于“后被中尚有顶点未被,则另选图中一个未曾被的顶点作起始点,重复上述过程,直至图中所有顶点都被到为止。换句话说,广度优先搜索遍历图的过程中以v为起始点,由近至远,依次和v有路径相通且路径长度为1,2,…的顶点。广度优先搜索和深度优先搜索类似,在遍历的过程中也需要一个标志数组。并且,voidBFSTraverse(GraphG){ for(v=0;v<G.vexnum;++v)visited[v]= for(v=0;v<G.vexnum;if }voidBFS(GraphG,intv) visited[v]=TRUE;Visit(v); //v //v入队列 //uif(!visited[w]){ }}}n个顶点的无向连通图,无论其生成树的形态如何,所有生成树中都有且仅有n-1条边。u1出发T的初值为T={}。uv成树构造完毕,这时集合T中包含了最小生成树的所有边。while(u,v)=min{wuv;u∈U,v∈V-U}T=T+{(u,Kruskal算法是一种按照网中边的权值递增的顺序构造最小生成树的方法。其基本思想G=(V,ET=(V,{}构成通分量。然后,按照边的权值由小到大的顺序,G的边集E中的各条边。若被的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为通分量;若被边的两个顶点属于同通分T1时,此连通分量便为G的一棵最小生成树。V长度最短者。其中,从源点到顶点vVuu到该顶点的路径长度值中的较小值。此过程不断重复,直到集合T的顶点全部加入到S中为止。Dijkstrax,那么,(v0,xxS中,那么必然存在另外的终点S中而路径长度比此路径还短的路径,这与我们按路径长度递增的顺序产生最短路径的vivviD[i]D[i],vjvk,则可想而知,是D[j]和从vj到vk的弧上的权值之和。其中,D[i或者弧(v,vi)D[kvk∈S和弧(vkvi)上的权值之和。若〈vivjedges[i][j]为∞(在计算机上可用允许的最大值代替。S为已找到从v出发的最短路径的终点的集合,它的初始状态为空集。那么,从v出发到图上其余各顶点D[i]=edges[LocateVex(G,v)][i]②选择vjD[j]+,,(即判别弧(vi,v0)和(v0,vj)是否存在。如果存在,则比较(vi,vj)和(vi,v0,vj)的路径长vivj0的最短路径。假如在路径上再增加v1(viv1)和(v1vj)分别是当前找到的中间顶点的序号0的最短路径,那么(viv1vj)vivj的中间顶点的序号不大于1vivj0的最短路径相比较,从中1v2,继续进行试探。依次类推。在一般情况下,若(vivk)和(vkvj)vivkvkvj的中间顶点的k-1的最短路径,则将(vivkvj)vivj且中间顶点序号k-1vivjk的最 从上述计算可见,D(1)[i][j]是从vi到vj的中间顶点的序号不大于1的最短路径的长度;Dk)[i][j]vivj的中间顶点的个数不大于k的最短路径的长度;Dn-1)[i][j]vi到vj的最短路径的长度。AOVjji的后继。若<i,j>ij的直接前驱,顶点j是顶点i的直接后驱。AOV网所代表的一项工程中活动的集合显然是一个偏序集合。为了保证该项工程得以i优先于顶点jji。O(e+nAOE销(如该活动持续的时间,则此带权的有向图称为AOE网。AOEvk的所有活动<vj,vk>都结束时,vk代表的才能发生;而活动<vj,vk>的最早结束时间为ve[j]+dut(<vj,(<, <vj 发的所有活动<vk,vj>的终点vj的最迟时间vl[j]。vl[k]的计算方法如下:(< vk 始。也就是说,活动ai的最早开始时间应等于vk的最早发生时间。因此,有: 由弧<vk,vj>>表示,则ai的最晚开始时间要保证vj的最迟发生时间不拖后。因此,应该 l[i]=e[i]l[i]>e[i]的活动则不是关键活动,l[i]-e[i]的如果得到的拓扑有序序列中顶点个数小于网中顶点数n,则说明网中存在环,不能求关键路vnvl[n-1]=ve[n-1],按逆拓扑有序求其余各顶点的最迟发生时间若某条弧满足条件e(s)=l(s),则为关键活动。接矩阵为A[1…n,1…n],且压缩在B[1…k],则k的值至少为(。 B.C.(n- D.n(n-分析:简单无向图的邻接矩阵是对称的,且对角线元素均是0,故压缩只须2n个结点、k条边的非连通无向图是一个森林(n>k,则该森林中必有() B. C.n- D.3G36条边的非连通无向图(不含自回路和多重边G至少有() B. C. D.通,则n=10。 【分析】当有向图中无回路时,从某顶点出发进行深度优先遍历时,出栈的顺序(n Pii个数据元素的概率,Cii个数据元素时,已经typedefstruct}// struct //}ST.elem[0].key=key; //设置“哨兵” //从后往前return //找不到时,i}nPi(n-1Pin nASL=∑─(n-i+1)=i=1 度,其为O(n)。折半查找要求查找表用顺序结构存放且各数据元素按关键字有序(升序或降序)排intSearch_Bin(SSTableST,KeyTypekey)low high //while(low<=high)mid=(low+high)/if(key mid;// if(key<ST.elem[mid].key)high=mid- //继续半区间进行查 lowmid //}return //}可以看到,查找表中任一元素的过程,即是判定树中从根到该元素结点路径上各结点关nk有2k-1-1<n≤2k-1,即k-1<log2(n+1)≤k,所以k= 所进行的关键码比较次数至多为log2(n+1)。。nnnASL=∑— —n=n+1log(n+1)-1≈log(n+1)- n if(!breturn elseif(b->data.key==key)returnb; elseif(key<b->data.key) }key,为将其插入,先voidInsertBST(BiTree {BiTreeif(bt==NULL){//递归结束条件s->data.key=key;}elseif(key<bdata.keyInsertBST(b //selseif(key>b }intDeleteBST(BiTree KeyTypekey)if return ifEQ(key,b->data.keyDelete //keyelseif(LT(key,b->data.key))DeleteBST(b->lchild,key); DeleteBST(b->rchild,key);return}}voidDelete(BiTree&p//if(!p->rchild) //右空则只需重接它的q=}elseifp->lchild //q=p=p->rchild;}else //左右均不q=s=p-while(!s->rchild){ q=s;s=s-}p->datas if(q!=p)q->rchild=s //重接*q的右elseq->lchilds //重接*q的左}}对给定序列建立二叉排序树,若左右均匀分布,则其查找过程类似于有序表的折半n个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的由关键字序列1,2,3,4,5构造而得的二叉排序树,ASL=(1+2+3+4+5)/5=3。3,1,2,5,4构造而得的二叉排序树,ASL(1+2+3+2+3)52.2。 和 行平衡化调整。设a结点为失去平衡的最小根结点,对该进行平衡化调整归纳起来h

c0BDBD

图RR型 结点a的平衡因子绝对值大于1,以结点a为根的失去平衡。由于结点c的左D可作为结点a的右,将结点a为根的调整为左是B,右是D,再将结点a为根的调整为结点c的左,结点c为新的根结点,如图(c)。a、c、E,若它们处于“\”直线上的同一个方向,则要作左单旋转,即以结点c为轴逆时针旋转。一个方向,则要作右单旋转,即以结点c为轴顺时针旋转,如图所示。h

cB aBD D 图LL型点b的右上,并使结点b的右高度增1,从而使结点a的平衡因子的绝对值大hh

GhFEFEEGhGh EGhGhxF xFh h

c EDFhEDFEh h-EhGhGhxhx

①首先,对结点b为根的,以结点c为轴,向左逆时针旋转,结点c成为该的直线上的同一个方向,则要作右单旋转,即以结点c为轴顺时针旋转,如图(c、f)。的关键码个数不超过树的深度。在平衡树上进行查找的时间复杂度为O(logn)。⑶除根结点之外的所有非终端结点至少有m/2棵键码均大于Kn,m/21≤n≤m1,n为关键码的个数。息指向的中去查找,当到达叶子结点时,则说明树中没有对应的关键码,查找失败。即B-树上的查找过程是一个顺指针查找结点和在结点中查找关键码交叉进行的过程。结点信息比在内存中进行关键码查找耗时多,所以,在磁盘上结点信息的次数,即B-树的层次树是决定B-树查找效率的首要因素。似分析。首先,讨论m阶B-数各层上的最少结点数。B-12个结点;由于除根结点外的每个非终端结点至少有m/2棵,则第三层至少有2(m/2)个结点;……;以树有n个关键码,则叶子结点即查找不成功的结点为n+1,由此有:nn即klogm2

12n上涉及的结点数不超过logm22.B+

2⑴有n棵的结点中含有n个关键码值≤Ki,则应继续在Ai所指中进行查找。B+树查找的分析类似于B-树。散列表与散列方法:选取某个函数,依该函数按关键码计算元素的位置,并按此存keykey与地址单元中元素关键码进行比n个数据元素的集合,总能找到关键码与存放地址一一对应的函数。若最大关键为m,可以分配m个数据元素存放单元,选取函数f(key)=key即可,但这样会造成空间的很大浪费,甚至不可能分配这么大的空间。通常关键码的集合比散列地址集合大得多,H(key 或者H(keyakey此法仅适合于:地址集合的大小==关键字集合的大小s位数字组成(k1,k2,kn),分析关键字集中的H(keykeyMOD p≤m表长pp很重要,若散H(key)=Random(key),其中,Random为伪随机函数。所谓开放定址法,即是由关键码得到的散列地址一旦产生了,也就是说,该地址已Hi=(Hash(key)+di)mod (1≤i<mHash(key)为散列函数,m为散列表长度,di1,2,……,m-1,且i11+2个散列地址的同义词,……邻的散列地址上“堆积”起来,大大降低了查找效率。为此,可采用二次探测法改善“堆积”问id12,-12,22,-22,……,q2,-q2q≤1mi2(行。链地址法适用于经常进行插入和删除的情况。对于给定值K,计算散列地址i=H(K),r[iNULL,则查找不成功若r[i].key=K,则查找成功否则求下一地址Hi,直至r[Hi]= 或r[Hi].key= 介绍的处理的方法中,产生后的查找仍然是给定值与关键码进行比较的过程。所以,查找过程中,关键码的比较次数,取决于产生的多少,产生的少,查找效率就高,产生的多,查找效率就低。因此,影响产生多少的因素,也就是影响查找效率散列表的装填因子定义为:α=────────────α是散列表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,填入表中的元素较多,产生的可能性就越大;α越小,填入表中的元素较1(1 1 11(1 1ln(1 12Unc)以后,要查找元素30要进行()次元素间的比较。 B. D. A.27 D.4、在常用的描述二叉排序树的结构中,关键字值最大的结点(左指针一定为空B.C.左右指针均为空D.分块查找成功的平均查找长度为(。 B. C. D.平均查找长度为23。 的平均探查次数不超过1.5,则散列表能够至少容纳()个表项。 8n2n的递增有序表,最少需要进行关键字比较()次。 突。假设装填因子α=0.75,散列函数的形式为H(K)=KMODP,回答下列问题:解答:由α=0.75,得表长m=11/0.75=15.

n个记录的序列{R1,R2,…,Rn},其相应关键字的序列是{K1,K2,…,Kn},相应p2,…,pn,使得相应关键字满足如下的非递减(或非递增)关系,即:Kp1≤Kp2≤…≤Kpn,n个记nnR[0]=R[i]; //设置“哨兵”forj=i-1;R[0].key<R[j].key; //return for(j=i-1;R[0].key<R[j].key;--j);R[j+1]=R[j](3)i2,3,n,voidInsertionSort(ElemR[ {for(i=2;i<=n;++i)R[0]= //为监视for(j=i-1;R[0].key< --jR[j+1] //R[j+1] //}}记录按关键码逐个比较得到的。平均情况下总比较次数约为n2/4。既然是在有序表中确定插n-R[n-n-R[n-无序序列R[1..n-序序列中关键字最大的记录“交换”R[n-i+1]的位置上。voidBubbleSort(ElemR[],inti iwhile(i>1)lastExchangeIndex=1;for(j=1;j<i;j++) (A[j+1]<A[j])lastExchangeIndex=}i=}} 2总比较次数(j1) n(n2i趟简单选择排序是,从无序序列R[i..n]的n-i+1记录中选出关键字最小的记录加入有序序列。n1个记录交换;第二趟,n-12ivoidSelectSort(ElemR[],intn){ //对记录序列R[1..n]作简单选择排序。for(i=1;i<n;++i){ //选择第i小的记录,并交换到位kSelectMinKey(R R[i..n]keyifi!=j //i}}稳定性:不同对简单选择排序的稳定性有争议,一般认为,若是从前往后比较来nn到()。排即是从这点出发给出插排序的改进方法。排序的基本思想是:先将待排序记录列分割成若干个“较稀疏的”子序列分1d1d2<1d2=1因子序列的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的步长因子序列的方法。步长因子序列可以有各种取法,有取奇数的,也11。intPartition1(ElemR[],intlow,inthigh)pivotkey //( //while(low<high&& //while(low<high&& //}retur

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论