数据结构与算法习题及答案 下载本文

精心整理

第1章绪论

习题

1.简述下列概念:数据、数据元素、数据项、数据对象、数据结构、逻辑结构、存储结构、抽象数据类型。 2.试举一个数据结构的例子,叙述其逻辑结构和存储结构两方面的含义和相互关系。 3.简述逻辑结构的四种基本关系并画出它们的关系图。 4.存储结构由哪两种基本的存储方法实现? 5.选择题 (1)在数据结构中,从逻辑上可以把数据结构分成()。 A.动态结构和静态结构B.紧凑结构和非紧凑结构 C.线性结构和非线性结构D.内部结构和外部结构 (2)与数据元素本身的形式、内容、相对位置、个数无关的是数据的()。 A.存储结构B.存储实现 C.逻辑结构D.运算实现 (3)通常要求同一逻辑结构中的所有数据元素具有相同的特性,这意味着()。 A.数据具有同一特点 B.不仅数据元素所包含的数据项的个数要相同,而且对应数据项的类型要一致 C.每个数据元素都一样 D.数据元素所包含的数据项的个数要相等 (4)以下说法正确的是()。 A.数据元素是数据的最小单位 B.数据项是数据的基本单位 C.数据结构是带有结构的各数据项的集合 D.一些表面上很不相同的数据可以有相同的逻辑结构 (5)以下与数据的存储结构无关的术语是()。 A.顺序队列B.链表C.有序表D.链栈 (6)以下数据结构中,()是非线性数据结构 A.树B.字符串C.队D.栈 6.试分析下面各程序段的时间复杂度。 (1)x=90;y=100;? while(y>0) if(x>100) {x=x-10;y--;} elsex++;

(2)for(i=0;i

for(j=0;j

(3)s=0; fori=0;i

for(j=0;j

s+=B[i][j];

sum=s;

精心整理

精心整理

(4)i=1; while(i<=n) i=i*3; (5)x=0;

for(i=1;i

x++;

(6)x=n;//n>1

y=0;

while(x≥(y+1)*(y+1)) y++;

(1)O(1) (2)O(m*n) (3)O(n2) (4)O(log3n) (5)因为x++共执行了n-1+n-2+……+1=n(n-1)/2,所以执行时间为O(n2) (6)O(n) 第2章线性表 1.选择题 (1)一个向量第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是()。 A.110B.108 C.100D.120 (2)在n个结点的顺序表中,算法的时间复杂度是O(1)的操作是()。 A.访问第i个结点(1≤i≤n)和求第i个结点的直接前驱(2≤i≤n) B.在第i个结点后插入一个新结点(1≤i≤n) C.删除第i个结点(1≤i≤n) D.将n个结点从小到大排序 (3)向一个有127个元素的顺序表中插入一个新元素并保持原来顺序不变,平均要移动的元素个数为()。 A.8B.63.5 C.63D.7 (4)链接存储的存储结构所占存储空间()。 A.分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针 B.只有一部分,存放结点值 C.只有一部分,存储表示结点间关系的指针

D.分两部分,一部分存放结点值,另一部分存放结点所占单元数

(5)线性表若采用链式存储结构时,要求内存中可用存储单元的地址()。 A.必须是连续的B.部分地址必须是连续的 C.一定是不连续的D.连续或不连续都可以

(6)线性表L在()情况下适用于使用链式结构实现。

A.需经常修改L中的结点值B.需不断对L进行删除插入 C.L中含有大量的结点D.L中结点结构复杂 (7)单链表的存储密度()。

A.大于1B.等于1 C.小于1D.不能确定 精心整理

精心整理

(8)将两个各有n个元素的有序表归并成一个有序表,其最少的比较次数是()。 A.nB.2n-1 C.2nD.n-1

(9)在一个长度为n的顺序表中,在第i个元素(1≤i≤n+1)之前插入一个新元素时须向后移动()个元素。

A.n-iB.n-i+1 C.n-i-1D.i

(10)线性表L=(a1,a2,……an),下列说法正确的是()。 A.每个元素都有一个直接前驱和一个直接后继 B.线性表中至少有一个元素

C.表中诸元素的排列必须是由小到大或由大到小

D.除第一个和最后一个元素外,其余每个元素都有一个且仅有一个直接前驱和直接后继。 (11)若指定有n个元素的向量,则建立一个有序单链表的时间复杂性的量级是()。 A.O(1)B.O(n)C.O(n2)D.O(nlog2n) (12)以下说法错误的是()。 A.求表长、定位这两种运算在采用顺序存储结构时实现的效率不比采用链式存储结构时实现的效率低 B.顺序存储的线性表可以随机存取 C.由于顺序存储要求连续的存储区域,所以在存储管理上不够灵活 D.线性表的链式存储结构优于顺序存储结构 (13)在单链表中,要将s所指结点插入到p所指结点之后,其语句应为()。 A.s->next=p+1;p->next=s; B.(*p).next=s;(*s).next=(*p).next; C.s->next=p->next;p->next=s->next; D.s->next=p->next;p->next=s; (14)在双向链表存储结构中,删除p所指的结点时须修改指针()。 A.p->next->prior=p->prior;p->prior->next=p->next; B.p->next=p->next->next;p->next->prior=p; C.p->prior->next=p;p->prior=p->prior->prior; D.p->prior=p->next->next;p->next=p->prior->prior; (15)在双向循环链表中,在p指针所指的结点后插入q所指向的新结点,其修改指针的操作是()。 A.p->next=q;q->prior=p;p->next->prior=q;q->next=q; B.p->next=q;p->next->prior=q;q->prior=p;q->next=p->next; C.q->prior=p;q->next=p->next;p->next->prior=q;p->next=q; D.q->prior=p;q->next=p->next;p->next=q;p->next->prior=q; 2.算法设计题 (1)将两个递增的有序链表合并为一个递增的有序链表。要求结果链表仍使用原来两个链表的存储空间,不另外占用其它的存储空间。表中不允许有重复的数据。 voidMergeList_L(LinkList&La,LinkList&Lb,LinkList&Lc){ pa=La->next;pb=Lb->next;

Lc=pc=La;//用La的头结点作为Lc的头结点 while(pa&&pb){

if(pa->datadata){pc->next=pa;pc=pa;pa=pa->next;} elseif(pa->data>pb->data){pc->next=pb;pc=pb;pb=pb->next;} else{//相等时取La的元素,删除Lb的元素 pc->next=pa;pc=pa;pa=pa->next; q=pb->next;deletepb;pb=q;} }

pc->next=pa?pa:pb;//插入剩余段 精心整理

精心整理

deleteLb;//释放Lb的头结点}

(2)将两个非递减的有序链表合并为一个非递增的有序链表。要求结果链表仍使用原来两个链表的存储空间,不另外占用其它的存储空间。表中允许有重复的数据。

voidunion(LinkList&La,LinkList&Lb,LinkList&Lc,){ pa=La->next;pb=Lb->next;//初始化

Lc=pc=La;//用La的头结点作为Lc的头结点 Lc->next=NULL; while(pa||pb){

if(!pa){q=pb;pb=pb->next;} elseif(!pb){q=pa;pa=pa->next;}

elseif(pa->data<=pb->data){q=pa;pa=pa->next;} else{q=pb;pb=pb->next;} q->next=Lc->next;Lc->next=q;//插入 } deleteLb;//释放Lb的头结点} (3)已知两个链表A和B分别表示两个集合,其元素递增排列。请设计算法求出A与B的交集,并存放于A链表中。 voidMix(LinkList&La,LinkList&Lb,LinkList&Lc,){ pa=la->next;pb=lb->next;∥设工作指针pa和pb; Lc=pc=La;//用La的头结点作为Lc的头结点 while(pa&&pb) if(pa->data==pb->data)∥交集并入结果表中。 {pc->next=pa;pc=pa;pa=pa->next; u=pb;pb=pb->next;deleteu;} elseif(pa->datadata){u=pa;pa=pa->next;deleteu;} else{u=pb;pb=pb->next;deleteu;} while(pa){u=pa;pa=pa->next;deleteu;}∥释放结点空间 while(pb){u=pb;pb=pb->next;deleteu;}∥释放结点空间 pc->next=null;∥置链表尾标记。 deleteLb;∥注:本算法中也可对B表不作释放空间的处理 (4)已知两个链表A和B分别表示两个集合,其元素递增排列。请设计算法求出两个集合A和B的差集(即仅由在A中出现而不在B中出现的元素所构成的集合),并以同样的形式存储,同时返回该集合的元素个数。 voidDifference(LinkedListA,B,*n) ∥A和B均是带头结点的递增有序的单链表,分别存储了一个集合,本算法求两集合的差集,存储于单链表A中,*n是结果集合中元素个数,调用时为0 {p=A->next;∥p和q分别是链表A和B的工作指针。 q=B->next;pre=A;∥pre为A中p所指结点的前驱结点的指针。 while(p!=null&&q!=null)

if(p->datadata){pre=p;p=p->next;*n++;}∥A链表中当前结点指针后移。 elseif(p->data>q->data)q=q->next;∥B链表中当前结点指针后移。 else{pre->next=p->next;∥处理A,B中元素值相同的结点,应删除。 u=p;p=p->next;deleteu;}∥删除结点

(5)设计算法将一个带头结点的单链表A分解为两个具有相同结构的链表B、C,其中B表的结点为A表中值小于零的结点,而C表的结点为A表中值大于零的结点(链表A的元素类型为整型,要求B、C表利用A表的结点)。

(6)设计一个算法,通过一趟遍历在单链表中确定值最大的结点。 精心整理

精心整理

ElemTypeMax(LinkListL){

if(L->next==NULL)returnNULL;

pmax=L->next;//假定第一个结点中数据具有最大值 p=L->next->next;

while(p!=NULL){//如果下一个结点存在 if(p->data>pmax->data)pmax=p; p=p->next; }

returnpmax->data;

(7)设计一个算法,通过遍历一趟,将链表中所有结点的链接方向逆转,仍利用原表的存储空间。 voidinverse(LinkList&L){ //逆置带头结点的单链表L p=L->next;L->next=NULL; while(p){ q=p->next;//q指向*p的后继 p->next=L->next; L->next=p;//*p插入在头结点之后 p=q; } } (8)设计一个算法,删除递增有序链表中值大于mink且小于maxk的所有元素(mink和maxk是给定的两个参数,其值可以和表中的元素相同,也可以不同)。 voiddelete(LinkList&L,intmink,intmaxk){ p=L->next;//首元结点 while(p&&p->data<=mink) {pre=p;p=p->next;}//查找第一个值>mink的结点 if(p){ while(p&&p->datanext; //查找第一个值≥maxk的结点 q=pre->next;pre->next=p;//修改指针 while(q!=p) {s=q->next;deleteq;q=s;}//释放结点空间 }//if } (9)已知p指向双向循环链表中的一个结点,其结点结构为data、prior、next三个域,写出算法change(p),交换p所指向的结点和它的前缀结点的顺序。 知道双向循环链表中的一个结点,与前驱交换涉及到四个结点(p结点,前驱结点,前驱的前驱结点,后继结点)六条链。

voidExchange(LinkedListp)

∥p是双向循环链表中的一个结点,本算法将p所指结点与其前驱结点交换。 {q=p->llink;

q->llink->rlink=p;∥p的前驱的前驱之后继为p p->llink=q->llink;∥p的前驱指向其前驱的前驱。 q->rlink=p->rlink;∥p的前驱的后继为p的后继。 q->llink=p;∥p与其前驱交换

p->rlink->llink=q;∥p的后继的前驱指向原p的前驱 精心整理

精心整理

p->rlink=q;∥p的后继指向其原来的前驱 }∥算法exchange结束。 (10)已知长度为n的线性表A采用顺序存储结构,请写一时间复杂度为O(n)、空间复杂度为O(1)的算法,该算法删除线性表中所有值为item的数据元素。

[题目分析]在顺序存储的线性表上删除元素,通常要涉及到一系列元素的移动(删第i个元素,第i+1至第n个元素要依次前移)。本题要求删除线性表中所有值为item的数据元素,并未要求元素间的相对位置不变。因此可以考虑设头尾两个指针(i=1,j=n),从两端向中间移动,凡遇到值item的数据元素时,直接将右端元素左移至值为item的数据元素位置。

voidDelete(ElemTypeA[],intn)

∥A是有n个元素的一维数组,本算法删除A中所有值为item的元素。 {i=1;j=n;∥设置数组低、高端指针(下标)。 while(i

A.iB.n-iC.n-i+1D.不确定 (3)数组Q[n]用来表示一个循环队列,f为当前队列头元素的前一位置,r为队尾元素的位置,假定队列中元素的个数小于n,计算队列中元素个数的公式为()。 A.r-fB.(n+f-r)%nC.n+r-fD.(n+r-f)%n (4)链式栈结点为:(data,link),top指向栈顶.若想摘除栈顶结点,并将删除结点的值保存到x中,则应执行操作()。

A.x=top->data;top=top->link; B.top=top->link;x=top->link; C.x=top;top=top->link; D.x=top->link; (5)设有一个递归算法如下 ??????intfact(intn){?//n大于等于0 ???????????if(n<=0)return1;

???????????elsereturnn*fact(n-1);??????}

则计算fact(n)需要调用该函数的次数为()。? A.?n+1?????B.?n-1?????C.n?????D.n+2 (6)栈在?()中有所应用。

A.递归调用B.函数调用C.表达式求值D.前三个选项都有 精心整理

精心整理

(7)为解决计算机主机与打印机间速度不匹配问题,通常设一个打印数据缓冲区。主机将要输出的数据依次写入该缓冲区,而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是()。

A.队列B.栈C.线性表D.有序表

(8)设栈S和队列Q的初始状态为空,元素e1、e2、e3、e4、e5和e6依次进入栈S,一个元素出栈后即进入Q,若6个元素出队的序列是e2、e4、e3、e6、e5和e1,则栈S的容量至少应该是( )。

A.2B.3 C.4D.6

(9)在一个具有n个单元的顺序栈中,假设以地址高端作为栈底,以top作为栈顶指针,则当作进栈处理时,top的变化为( )。

A.top不变B.top=0 C.top--D.top++

(10)设计一个判别表达式中左,右括号是否配对出现的算法,采用( )数据结构最佳。 A.线性表的顺序存储结构B.队列 C.线性表的链式存储结构D.栈 (11)用链接方式存储的队列,在进行删除运算时( )。 A.仅修改头指针B.仅修改尾指针 C.头、尾指针都要修改D.头、尾指针可能都要修改 (12)循环队列存储在数组A[0..m]中,则入队时的操作为( )。 A.rear=rear+1B.rear=(rear+1)%(m-1) C.rear=(rear+1)%mD.rear=(rear+1)%(m+1) (13)最大容量为n的循环队列,队尾指针是rear,队头是front,则队空的条件是( )。 A.(rear+1)%n==frontB.rear==front C.rear+1==frontD.(rear-l)%n==front (14)栈和队列的共同点是( )。 A.都是先进先出B.都是先进后出 C.只允许在端点处插入和删除元素D.没有共同点 (15)一个递归算法必须包括( )。 A.递归部分B.终止条件和递归部分 C.迭代部分D.终止条件和迭代部分 (2)回文是指正读反读均相同的字符序列,如“abba”和“abdba”均是回文,但“good”不是回文。试写一个算法判定给定的字符向量是否为回文。(提示:将一半字符入栈)? 根据提示,算法可设计为: //以下为顺序栈的存储结构定义 #defineStackSize100//假定预分配的栈空间最多为100个元素 typedefcharDataType;//假定栈元素的数据类型为字符

typedefstruct{

DataTypedata[StackSize];

inttop;

精心整理

精心整理

}SeqStack;?

intIsHuiwen(char*t)

{//判断t字符向量是否为回文,若是,返回1,否则返回0

SeqStacks;

inti,len; chartemp; InitStack(&s); len=strlen(t);//求向量长度 for(i=0;i

}?

return1;//比较完毕均相等则返回1

}

精心整理

精心整理

(3)设从键盘输入一整数的序列:a1,a2,a3,…,an,试编写算法实现:用栈结构存储输入的整数,当ai≠-1时,将ai进栈;当ai=-1时,输出栈顶整数并出栈。算法应对异常情况(入栈满等)给出相应的信息。 #definemaxsize栈空间容量 voidInOutS(ints[maxsize])

//s是元素为整数的栈,本算法进行入栈和退栈操作。 {inttop=0;//top为栈顶指针,定义top=0时为栈空。 for(i=1;i<=n;i++)//n个整数序列作处理。 {scanf(“%d”,&x);//从键盘读入整数序列。 if(x!=-1)//读入的整数不等于-1时入栈。

if(top==maxsize-1){printf(“栈满\\n”);exit(0);}elses[++top]=x;//x入栈。 else//读入的整数等于-1时退栈。

{if(top==0){printf(“栈空\\n”);exit(0);}elseprintf(“出栈元素是%d\\n”,s[top--]);}} }//算法结束。 (4)从键盘上输入一个后缀表达式,试编写算法计算表达式的值。规定:逆波兰表达式的长度不超过一行,以$符作为输入结束,操作数之间用空格分隔,操作符只可能有+、-、*、/四种运算。例如:23434+2*$。

[题目分析]逆波兰表达式(即后缀表达式)求值规则如下:设立运算数栈OPND,对表达式从左到右扫描(读入),当表达式中扫描到数时,压入OPND栈。当扫描到运算符时,从OPND退出两个数,进行相应运算,结果再压入OPND栈。这个过程一直进行到读出表达式结束符$,这时OPND栈中只有一个数,就是结果。 floatexpr() //从键盘输入逆波兰表达式,以‘$’表示输入结束,本算法求逆波兰式表达式的值。 {floatOPND[30];//OPND是操作数栈。 init(OPND);//两栈初始化。 floatnum=0.0;//数字初始化。 scanf(“%c”,&x);//x是字符型变量。 while(x!=’$’) {switch {case‘0’<=x<=’9’:while((x>=’0’&&x<=’9’)||x==’.’)//拼数 if(x!=’.’)//处理整数 {num=num*10+(ord(x)-ord(‘0’));scanf(“%c”,&x);} else//处理小数部分。 {scale=10.0;scanf(“%c”,&x); while(x>=’0’&&x<=’9’) {num=num+(ord(x)-ord(‘0’)/scale; scale=scale*10;scanf(“%c”,&x);} }//else push(OPND,num);num=0.0;//数压入栈,下个数初始化 casex=‘’:break;//遇空格,继续读下一个字符。 casex=‘+’:push(OPND,pop(OPND)+pop(OPND));break;

casex=‘-’:x1=pop(OPND);x2=pop(OPND);push(OPND,x2-x1);break; casex=‘*’:push(OPND,pop(OPND)*pop(OPND));break;

casex=‘/’:x1=pop(OPND);x2=pop(OPND);push(OPND,x2/x1);break; default://其它符号不作处理。 }//结束switch

scanf(“%c”,&x);//读入表达式中下一个字符。 }//结束while(x!=‘$’)

printf(“后缀表达式的值为%f”,pop(OPND)); 精心整理

精心整理

}//算法结束。

[算法讨论]假设输入的后缀表达式是正确的,未作错误检查。算法中拼数部分是核心。若遇到大于等于‘0’且小于等于‘9’的字符,认为是数。这种字符的序号减去字符‘0’的序号得出数。对于整数,每读入一个数字字符,前面得到的部分数要乘上10再加新读入的数得到新的部分数。当读到小数点,认为数的整数部分已完,要接着处理小数部分。小数部分的数要除以10(或10的幂数)变成十分位,百分位,千分位数等等,与前面部分数相加。在拼数过程中,若遇非数字字符,表示数已拼完,将数压入栈中,并且将变量num恢复为0,准备下一个数。这时对新读入的字符进入‘+’、‘-’、‘*’、‘/’及空格的判断,因此在结束处理数字字符的case后,不能加入break语句。

(5)假设以I和O分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由I和O组成的序列,称可以操作的序列为合法序列,否则称为非法序列。

①下面所示的序列中哪些是合法的?

A.IOIIOIOOB.IOOIOIIOC.IIIOIOIOD.IIIOOIOO ②通过对①的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回true,否则返回false(假定被判定的操作序列已存入一维数组中)。 ①A和D是合法序列,B和C是非法序列。 ②设被判定的操作序列已存入一维数组A中。 intJudge(charA[]) //判断字符数组A中的输入输出序列是否是合法序列。如是,返回true,否则返回false。 {i=0;//i为下标。 j=k=0;//j和k分别为I和字母O的的个数。 while(A[i]!=‘\\0’)//当未到字符数组尾就作。 {switch(A[i]) {case‘I’:j++;break;//入栈次数增1。 case‘O’:k++;if(k>j){printf(“序列非法\\n”);exit(0);} }

i++;//不论A[i]是‘I’或‘O’,指针i均后移。} if(j!=k){printf(“序列非法\\n”);return(false);} else{printf(“序列合法\\n”);return(true);} }//算法结束。 [算法讨论]在入栈出栈序列(即由‘I’和‘O’组成的字符串)的任一位置,入栈次数(‘I’的个数)都必须大于等于出栈次数(即‘O’的个数),否则视作非法序列,立即给出信息,退出算法。整个序列(即读到字符数组中字符串的结束标记‘\\0’),入栈次数必须等于出栈次数(题目中要求栈的初态和终态都为空),否则视为非法序列。 (6)假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素站点(注意不设头指针),试编写

相应的置空队、判队空、入队和出队等算法。?

算法如下:

//先定义链队结构:

typedefstructqueuenode{

Datatypedata;

精心整理

精心整理

structqueuenode*next;

}QueueNode;//以上是结点类型的定义

typedefstruct{

queuenode*rear;

}LinkQueue;//只设一个指向队尾元素的指针

(1)置空队 voidInitQueue(LinkQueue*Q) {//置空队:就是使头结点成为队尾元素 QueueNode*s; Q->rear=Q->rear->next;//将队尾指针指向头结点 while(Q->rear!=Q->rear->next)//当队列非空,将队中元素逐个出队 {s=Q->rear->next; Q->rear->next=s->next; free(s);

}//回收结点空间

}

精心整理

精心整理

(2)判队空?

intEmptyQueue(LinkQueue*Q)

{//判队空

//当头结点的next指针指向自己时为空队

returnQ->rear->next->next==Q->rear->next;

}

(3)入队 voidEnQueue(LinkQueue*Q,Datatypex) {//入队 //也就是在尾结点处插入元素 QueueNode*p=(QueueNode*)malloc(sizeof(QueueNode));//申请新结点 p->data=x;p->next=Q->rear->next;//初始化新结点并链入 Q-rear->next=p;? Q->rear=p;//将尾指针移至新结点 }

(4)出队

DatatypeDeQueue(LinkQueue*Q)

精心整理

精心整理

{//出队,把头结点之后的元素摘下

Datatypet;

QueueNode*p;

if(EmptyQueue(Q))

Error(\

p=Q->rear->next->next;//p指向将要摘下的结点 x=p->data;//保存结点中数据 if(p==Q->rear) {//当队列中只有一个结点时,p结点出队后,要将队尾指针指向头结点 Q->rear=Q->rear->next;Q->rear->next=p->next;} else? Q->rear->next->next=p->next;//摘下结点p free(p);//释放被删结点 returnx; } (7)假设以数组Q[m]存放循环队列中的元素,同时设置一个标志tag,以tag==0和tag==1来区别在队头指针(front)和队尾指针(rear)相等时,队列状态为“空”还是“满”。试编写与此结构相应的插入(enqueue)和删除(dlqueue)算法。

【解答】

循环队列类定义

#include

templateclassQueue{ public: Queue(int=10); ~Queue(){delete[]Q;}

//循环队列的类定义

精心整理

精心整理

voidEnQueue(Type&item); TypeDeQueue(); TypeGetFront();

voidMakeEmpty(){front=rear=tag=0;}

//置空队列

intIsEmpty()const{returnfront==rear&&tag==0;} //判队列空否 intIsFull()const{returnfront==rear&&tag==1;} //判队列满否 private: intrear,front,tag; Type*Q; intm; }

//队尾指针、队头指针和队满标志

//存放队列元素的数组 //队列最大可容纳元素个数

构造函数 template Queue::Queue(intsz):rear(0),front(0),tag(0),m(sz){ //建立一个最大具有m个元素的空队列。 Q=newType[m]; assert(Q!=0); } //创建队列空间 //断言:动态存储分配成功与否

插入函数 template voidQueue::EnQueue(Type&item){ } assert(!IsFull()); rear=(rear+1)%m; Q[rear]=item; tag=1; //判队列是否不满,满则出错处理 //队尾位置进1,队尾指针指示实际队尾位置 //进队列 //标志改1,表示队列不空

删除函数 template TypeQueue::DeQueue(){ assert(!IsEmpty()); front=(front+1)%m; tag=0; returnQ[front]; //判断队列是否不空,空则出错处理 //队头位置进1,队头指针指示实际队头的前一位置 //标志改0,表示栈不满 //返回原队头元素的值 }

读取队头元素函数 template TypeQueue::GetFront(){ }

assert(!IsEmpty());

//判断队列是否不空,空则出错处理

//返回队头元素的值

returnQ[(front+1)%m];

(8)如果允许在循环队列的两端都可以进行插入和删除操作。要求: ①写出循环队列的类型定义;

②写出“从队尾删除”和“从队头插入”的算法。

[题目分析]用一维数组v[0..M-1]实现循环队列,其中M是队列长度。设队头指针front和队尾指针rear,约定front指向队头元素的前一位置,rear指向队尾元素。定义front=rear时为队空,(rear+1)%m=front为队满。约定队头端入队向下标小的方向发展,队尾端入队向下标大的方向发展。 精心整理

精心整理

(1)#defineM队列可能达到的最大长度 typedefstruct {elemtpdata[M]; intfront,rear; }cycqueue;

(2)elemtpdelqueue(cycqueueQ)

//Q是如上定义的循环队列,本算法实现从队尾删除,若删除成功,返回被删除元素,否则给出出错信息。

{if(Q.front==Q.rear){printf(“队列空”);exit(0);} Q.rear=(Q.rear-1+M)%M;//修改队尾指针。

return(Q.data[(Q.rear+1+M)%M]);//返回出队元素。 }//从队尾删除算法结束

voidenqueue(cycqueueQ,elemtpx) //Q是顺序存储的循环队列,本算法实现“从队头插入”元素x。 {if(Q.rear==(Q.front-1+M)%M){printf(“队满”;exit(0);) Q.data[Q.front]=x;//x入队列 Q.front=(Q.front-1+M)%M;//修改队头指针。 }//结束从队头插入算法。 (9)已知Ackermann函数定义如下: ①写出计算Ack(m,n)的递归算法,并根据此算法给出出Ack(2,1)的计算过程。 ②写出计算Ack(m,n)的非递归算法。 intAck(intm,n) {if(m==0)return(n+1); elseif(m!=0&&n==0)return(Ack(m-1,1)); elsereturn(Ack(m-1,Ack(m,m-1)); }//算法结束 (1)Ack(2,1)的计算过程 Ack(2,1)=Ack(1,Ack(2,0))//因m<>0,n<>0而得 =Ack(1,Ack(1,1))//因m<>0,n=0而得 =Ack(1,Ack(0,Ack(1,0)))//因m<>0,n<>0而得 =Ack(1,Ack(0,Ack(0,1)))//因m<>0,n=0而得 =Ack(1,Ack(0,2))//因m=0而得 =Ack(1,3)//因m=0而得 =Ack(0,Ack(1,2))//因m<>0,n<>0而得 =Ack(0,Ack(0,Ack(1,1)))//因m<>0,n<>0而得 =Ack(0,Ack(0,Ack(0,Ack(1,0))))//因m<>0,n<>0而得 =Ack(0,Ack(0,Ack(0,Ack(0,1))))//因m<>0,n=0而得 =Ack(0,Ack(0,Ack(0,2)))//因m=0而得 =Ack(0,Ack(0,3))//因m=0而得 =Ack(0,4)//因n=0而得 =5//因n=0而得

(2)intAckerman(intm,intn) {intakm[M][N];inti,j;

for(j=0;j

{akm[i][0]=akm[i-1][1]; for(j=1;j

精心整理

akm[i][j]=akm[i-1][akm[i][j-1]]; }

return(akm[m][n]); }//算法结束

(10)已知f为单链表的表头指针,链表中存储的都是整型数据,试写出实现下列运算的递归算法: ①求链表中的最大整数; ②求链表的结点个数; ③求所有整数的平均值。

#include classList; friendclassList; private:

}; classList{ private:

};

}

voidList::NewList(constintretvalue){

first=NULL;intvalue;ListNode*q; cout<<\; cin>>value;

while(value!=retvalue){

//提示 //输入 //输入有效

//建立包含value的新结点

//建立链表,以输入retvalue结束

ListNode*first,current; intMax(ListNode*f); intNum(ListNode*f); floatAvg(ListNode*f,int&n); List():first(NULL),current(NULL){} ~List(){} ListNode*NewNode(constintitem); voidNewList(constintretvalue); voidPrintList(); //构造函数 //析构函数 //创建链表结点,其值为item //链表类 intdata; //结点数据 //结点指针 //构造函数 ListNode*link;

//链表结点类

classListNode{

//定义在头文件\中

ListNode(constintitem):data(item),link(NULL){} public: //建立链表,以输入retvalue结束 //输出链表所有结点数据 //求链表所有数据的最大值 //求链表中数据个数 //求链表所有数据的平均值 //创建新链表结点 intGetMax(){returnMax(first);} intGetNum(){returnNum(first);} floatGetAvg(){returnAvg(first);} ListNode*List::NewNode(constintitem){ returnnewnode; ListNode*newnode=newListNode(item); q=NewNode(value);

if(first==NULL)first=current=q; else{current->link=q;current=q;}

//空表时,新结点成为链表第一个结点 //非空表时,新结点链入链尾

精心整理

精心整理

}

voidList::PrintList(){

}

intList::Max(ListNode*f){

}

intList::Num(ListNode*f){

}

floatList::Avg(ListNode*f,int&n){

if(f->link==NULL) //递归算法:求链表中所有元素的平均值 //链表中只有一个结点,递归结束条件 if(f==NULL)return0; return1+Num(f->link); //递归算法:求链表中结点个数 //空表,返回0 //否则,返回后继链表结点个数加1 //递归算法:求链表中的最大值 //递归结束条件 //在当前结点的后继链表中求最大值 //如果当前结点的值还要大,返回当前检点值 //否则返回后继链表中的最大值 ListNode*p=first; while(p!=NULL){ cout<<‘\\n’;

cout<data<<'';p=p->link;

}

//输出链表

cout<<\; }

current->link=NULL;

//链尾封闭

cin>>value;

//再输入

if(f->link==NULL)returnf->data; inttemp=Max(f->link); if(f->data>temp)returnf->data; elsereturntemp; {n=1;return(float)(f->data);}

else{floatSum=Avg(f->link,n)*n;n++;return(f->data+Sum)/n;} } #include\

Listtest;intfinished;

cout<<“输入建表结束标志数据:”; cin>>finished;

test.PrintList();

//输入建表结束标志数据 //打印链表

test.NewList(finished);

//建立链表

//定义在主文件中 intmain(intargc,char*argv[]){ cout<<\test.GetMax(); cout<<\test.GetNum(); cout<<\test.GetAve()<<'\\n'; printf(\return0;

精心整理

精心整理

}

第4章串、数组和广义表

习题

1.选择题

(1)串是一种特殊的线性表,其特殊性体现在()。 A.可以顺序存储B.数据元素是一个字符

C.可以链式存储D.数据元素可以是多个字符若 (2)串下面关于串的的叙述中,()是不正确的? A.串是字符的有限序列B.空串是由空格构成的串 C.模式匹配是串的一种重要运算D.串既可以采用顺序存储,也可以采用链式存储 (3)串“ababaaababaa”的next数组为()。 A...0456D. (4)串“ababaabab”的nextval为()。 A.B. C..010101011 (5)串的长度是指()。 A.串中所含不同字母的个数B.串中所含字符的个数 C.串中所含不同字符的个数D.串中所含非空格字符的个数 (6)假设以行序为主序存储二维数组A=array[1..100,1..100],设每个数据元素占2个存储单元,基地址为10,则LOC[5,5]=()。 A.808B.818 C.1010D.1020 (7)设有数组A[i,j],数组的每个元素长度为3字节,i的值为1到8,j的值为1到10,数组从内存首地址BA开始顺序存放,当用以列为主存放时,元素A[5,8]的存储首地址为()。 A.BA+141B.BA+180 C.BA+222D.BA+225 (8)设有一个10阶的对称矩阵A,采用压缩存储方式,以行序为主存储,a11为第一元素,其存储地址为1,每个元素占一个地址空间,则a85的地址为()。 A.13B.33 C.18D.40 (9)若对n阶对称矩阵A以行序为主序方式将其下三角形的元素(包括主对角线上所有元素)依次存放于一维数组B[1..(n(n+1))/2]中,则在B中确定aij(i

A.i(i-1)/2+jB.j(j-1)/2+iC.i(j-i)/2+1D.j(i-1)/2+1

(11)设二维数组A[1..m,1..n](即m行n列)按行存储在数组B[1..m*n]中,则二维数组元素A[i,j]在一维数组B中的下标为()。

A.(i-1)*n+jB.(i-1)*n+j-1 C.i*(j-1)D.j*m+i-1 (12)数组A[0..4,-1..-3,5..7]中含有元素的个数()。 A.55B.45 C.36D.16

(13)广义表A=(a,b,(c,d),(e,(f,g))),则Head(Tail(Head(Tail(Tail(A)))))的值为()。 A.(g)B.(d)C.cD.d

(14)广义表((a,b,c,d))的表头是(),表尾是()。 A.aB.()C.(a,b,c,d)D.(b,c,d) 精心整理

精心整理

(15)设广义表L=((a,b,c)),则L的长度和深度分别为()。 A.1和1B.1和3 C.1和2D.2和3

(1)已知模式串t=‘abcaabbabcab’写出用KMP法求得的每个字符对应的next和nextval函数值。 模式串t的next和nextval值如下: j t串 next[j] nextval[j] 123456789101112 abcaabbabcab 011122312345 011021301105 (2)设目标为t=“abcaabbabcabaacbacba”,模式为p=“abcabaa” ①计算模式p的naxtval函数值;

②不写出算法,只画出利用KMP算法进行模式匹配时每一趟的匹配过程。 ①p的nextval函数值为0110132。(p的next函数值为0111232)。 ②利用KMP(改进的nextval)算法,每趟匹配过程如下: 第一趟匹配:abcaabbabcabaacbacba abcab(i=5,j=5) 第二趟匹配:abcaabbabcabaacbacba abc(i=7,j=3) 第三趟匹配:abcaabbabcabaacbacba a(i=7,j=1) 第四趟匹配:abcaabbabcabaacbacba (成功)abcabaa(i=15,j=8) (3)数组A中,每个元素A[i,j]的长度均为32个二进位,行下标从-1到9,列下标从1到11,从首地址S开始连续存放主存储器中,主存储器字长为16位。求: ①存放该数组所需多少单元? ②存放数组第4列所有元素至少需多少单元? ③数组按行存放时,元素A[7,4]的起始地址是多少? ④数组按列存放时,元素A[4,7]的起始地址是多少? 每个元素32个二进制位,主存字长16位,故每个元素占2个字长,行下标可平移至1到11。 (1)242(2)22(3)s+182(4)s+142 (4)请将香蕉banana用工具H()—Head(),T()—Tail()从L中取出。 L=(apple,(orange,(strawberry,(banana)),peach),pear) H(H(T(H(T(H(T(L))))))) (5)写一个算法统计在输入字符串中各个不同字符出现的频度并将结果存入文件(字符串中的合法字符为A-Z这26个字母和0-9这10个数字)。 voidCount() //统计输入字符串中数字字符和字母字符的个数。 {inti,num[36]; charch;

for(i=0;i<36;i++)num[i]=0;//初始化 while((ch=getchar())!=‘#’)//‘#’表示输入字符串结束。 if(‘0’<=ch<=‘9’){i=ch-48;num[i]++;}//数字字符 else if(‘A’<=ch<=‘Z’){i=ch-65+10;num[i]++;}//字母字符 for(i=0;i<10;i++)//输出数字字符的个数 printf(“数字%d的个数=%d\\n”,i,num[i]); for(i=10;i<36;i++)//求出字母字符的个数 printf(“字母字符%c的个数=%d\\n”,i+55,num[i]); 精心整理

精心整理

}//算法结束。

(6)写一个递归算法来实现字符串逆序存储,要求不另设串存储空间。

[题目分析]实现字符串的逆置并不难,但本题“要求不另设串存储空间”来实现字符串逆序存储,即第一个输入的字符最后存储,最后输入的字符先存储,使用递归可容易做到。

voidInvertStore(charA[]) //字符串逆序存储的递归算法。

{charch;

staticinti=0;//需要使用静态变量 scanf(\

if(ch!='.')//规定'.'是字符串输入结束标志 {InvertStore(A);

A[i++]=ch;//字符串逆序存储 } A[i]='\\0';//字符串结尾标记 }//结束算法InvertStore。 (7)编写算法,实现下面函数的功能。函数voidinsert(char*s,char*t,intpos)将字符串t插入到字符串s中,插入位置为pos。假设分配给字符串s的空间足够让字符串t插入。(说明:不得使用任何库函数)

[题目分析]本题是字符串的插入问题,要求在字符串s的pos位置,插入字符串t。首先应查找字符串s的pos位置,将第pos个字符到字符串s尾的子串向后移动字符串t的长度,然后将字符串t复制到字符串s的第pos位置后。 对插入位置pos要验证其合法性,小于1或大于串s的长度均为非法,因题目假设给字符串s的空间足够大,故对插入不必判溢出。 voidinsert(char*s,char*t,intpos) //将字符串t插入字符串s的第pos个位置。 {inti=1,x=0;char*p=s,*q=t;//p,q分别为字符串s和t的工作指针 if(pos<1){printf(“pos参数位置非法\\n”);exit(0);} while(*p!=’\\0’&&i=pos;j--){*(p+x)=*p;p--;}//串s的pos后的子串右移,空出串t的位置。 q--;//指针q回退到串t的最后一个字符 for(j=1;j<=x;j++)*p--=*q--;//将t串插入到s的pos位置上 [算法讨论]串s的结束标记('\\0')也后移了,而串t的结尾标记不应插入到s中。 (8)已知字符串S1中存放一段英文,写出算法format(s1,s2,s3,n),将其按给定的长度n格式化成两端对齐的字符串S2,其多余的字符送S3。

[题目分析]本题要求字符串s1拆分成字符串s2和字符串s3,要求字符串s2“按给定长度n格式化成两端对齐的字符串”,即长度为n且首尾字符不得为空格字符。算法从左到右扫描字符串s1,找到第一个非空格字符,计数到n,第n个拷入字符串s2的字符不得为空格,然后将余下字符复制到字符串s3中。 voidformat(char*s1,*s2,*s3)

//将字符串s1拆分成字符串s2和字符串s3,要求字符串s2是长n且两端对齐 {char*p=s1,*q=s2; inti=0;

while(*p!='\\0'&&*p=='')p++;//滤掉s1左端空格 精心整理

精心整理

if(*p=='\\0'){printf(\字符串s1为空串或空格串\\n\}

while(*p!='\\0'&&i

while(*p==''&&*p!='\\0')p++;//往后查找一个非空格字符作串s2的尾字符 if(*p=='\\0'){printf(\串没有%d个两端对齐的字符串\\n\} *q=*p;//字符串s2最后一个非空字符 *(++q)='\\0';//置s2字符串结束标记 }

*q=s3;p++;//将s1串其余部分送字符串s3。 while(*p!='\\0'){*q=*p;q++;p++;} *q='\\0';//置串s3结束标记 }

(9)设二维数组a[1..m,1..n]含有m*n个整数。 ①写一个算法判断a中所有元素是否互不相同?输出相关信息(yes/no); ②试分析算法的时间复杂度。 [题目分析]判断二维数组中元素是否互不相同,只有逐个比较,找到一对相等的元素,就可结论为不是互不相同。如何达到每个元素同其它元素比较一次且只一次?在当前行,每个元素要同本行后面的元素比较一次(下面第一个循环控制变量p的for循环),然后同第i+1行及以后各行元素比较一次,这就是循环控制变量k和p的二层for循环。 intJudgEqual(inga[m][n],intm,n) //判断二维数组中所有元素是否互不相同,如是,返回1;否则,返回0。 {for(i=0;i

4

总的时间复杂度是O(n)。

(10)设任意n个整数存放于数组A(1:n)中,试编写算法,将所有正数排在所有负数前面(要求算法复杂性为0(n))。

[题目分析]本题属于排序问题,只是排出正负,不排出大小。可在数组首尾设两个指针i和j,i自小至大搜索到负数停止,j自大至小搜索到正数停止。然后i和j所指数据交换,继续以上过程,直到i=j为止。

voidArrange(intA[],intn)

//n个整数存于数组A中,本算法将数组中所有正数排在所有负数的前面 {inti=0,j=n-1,x;//用类C编写,数组下标从0开始 精心整理

精心整理

while(i

{while(i0)i++; while(i

if(i

}//算法Arrange结束.

[算法讨论]对数组中元素各比较一次,比较次数为n。最佳情况(已排好,正数在前,负数在后)不发生交换,最差情况(负数均在正数前面)发生n/2次交换。用类c编写,数组界偶是0..n-1。空间复杂度为O(1).

第5章树和二叉树 1.选择题 (1)把一棵树转换为二叉树后,这棵二叉树的形态是()。 A.唯一的B.有多种 C.有多种,但根结点都没有左孩子D.有多种,但根结点都没有右孩子 (2)由3个结点可以构造出多少种不同的二叉树?() A.2B.3 C.4D.5 (3)一棵完全二叉树上有1001个结点,其中叶子结点的个数是()。 A.250B.500 C.254D.501 (4)一个具有1025个结点的二叉树的高h为()。 A.11B.10 C.11至1025之间D.10至1024之间 (5)深度为h的满m叉树的第k层有()个结点。(1=

A.前序B.中序C.后序D.按层次 (9)在下列存储形式中,()不是树的存储形式? A.双亲表示法B.孩子链表表示法C.孩子兄弟表示法D.顺序存储表示法 (10)一棵非空的二叉树的先序遍历序列与后序遍历序列正好相反,则该二叉树一定满足()。 A.所有的结点均无左孩子B.所有的结点均无右孩子 C.只有一个叶子结点D.是任意一棵二叉树

(11)某二叉树的前序序列和后序序列正好相反,则该二叉树一定是()的二叉树。 A.空或只有一个结点B.任一结点无左子树 C.高度等于其结点数D.任一结点无右子树

(12)若X是二叉中序线索树中一个有左孩子的结点,且X不为根,则X的前驱为()。 A.X的双亲B.X的右子树中最左的结点

C.X的左子树中最右结点D.X的左子树中最右叶结点 (13)引入二叉线索树的目的是()。

A.加快查找结点的前驱或后继的速度B.为了能在二叉树中方便的进行插入与删除 精心整理

精心整理

C.为了能方便的找到双亲D.使二叉树的遍历结果唯一 (14)线索二叉树是一种()结构。

A.逻辑B.逻辑和存储C.物理D.线性

(15)设F是一个森林,B是由F变换得的二叉树。若F中有n个非终端结点,则B中右指针域为空的结点有()个。

A.n-1B.nC.n+1D.n+2 2.应用题

(1)试找出满足下列条件的二叉树

①先序序列与后序序列相同②中序序列与后序序列相同 ③先序序列与中序序列相同④中序序列与层次遍历序列相同 先序遍历二叉树的顺序是“根—左子树—右子树”,中序遍历“左子树—根—右子树”,后序遍历顺序是:“左子树—右子树―根",根据以上原则,本题解答如下: (1)?若先序序列与后序序列相同,则或为空树,或为只有根结点的二叉树 (2)?若中序序列与后序序列相同,则或为空树,或为任一结点至多只有左子树的二叉树. (3)?若先序序列与中序序列相同,则或为空树,或为任一结点至多只有右子树的二叉树. (4)?若中序序列与层次遍历序列相同,则或为空树,或为任一结点至多只有右子树的二叉树 (2)设一棵二叉树的先序序列:ABDFCEGH,中序序列:BFDAGEHC ①画出这棵二叉树。 ②画出这棵二叉树的后序线索树。 ③将这棵二叉树转换成对应的树(或森林)。 ? AC(1)(2) (3)假设用于通信的电文仅由8个字母组成,字母在电文中出现的频率分别为0.19,0.020.06,0.32,BD0.07,EH,0.03,0.21,0.10。 FG①试为这8个字母设计赫夫曼编码。 ②试设计另一种由二进制表示的等长编码方案。 ③对于上述实例,比较两种方案的优缺点。 (解:方案1;哈夫曼编码 先将概率放大100倍,以方便构造哈夫曼树。 w={7,19,2,6,32,3,21,10},按哈夫曼规则:【[(2,3),6],(7,10)】,……19,21,32 (100) 01 (40)(60) 192132(28) 0101 (17) (11) 19 2132 0 1 7106(5) 0101 23 7 106 0 1 方案比较: 23 字母对应出现字母对应出现方案1的WPL=2(0.19+0.32+0.21)+4(0.07+0.06+0.10)+5(0.02+0.03)=1.44+0.92+0.25=2.61 编号 编码 频率 编号 编码 频率 方案2的WPL=3(0.19+0.32+0.21+0.07+0.06+0.10+0.02+0.03)=3 1 1100 0.07 1 000 0.07 结论:哈夫曼编码优于等长二进制编码 2 00 0.19 2 001 0.19 ?(4)已知下列字符A、B、C、D、E、F、G的权值分别为3、12、7、4、2、8,11,试填写出其对应哈夫曼3 11110 0.02 3 010 0.02 树HT的存储结构的初态和终态。 4 1110 0.06 4 011 0.06 初态: 5 10 0.32 5 100 0.32 weight parent lchild rchild 6 101 0.03 1 3 0 0 0 精心整理 精心整理 2 3 4 5 6 7 8 9 10 11 12 13 是否是叶子返回1 12 7 4 2 1 8 2 11 3 4 5 6 7 8 9 10 11 12 13 0 0 0 weight 0 3 0 12 0 7 0 4 0 2 0 8 0 11 0 5 0 9 15 20 27 47 0 0 0 parent 0 8 0 12 0 10 0 9 0 8 0 10 0 11 0 9 0 11 12 13 13 0 0 0 0 lchild 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 4 3 9 2 11 终态 3.算法设计题 以二叉链表作为二叉树的存储结构,编写rchild 以下算法: (1)统计二叉树的叶结点个数。 0 intLeafNodeCount(BiTreeT) 0 { 0 if(T==NULL) 0 return0;//如果是空树,0 则叶子结点个数为0 0 elseif(T->lchild==NULL&&T0 ->rchild==NULL) 1 return1;//判断该结点8 结点(左孩子右孩子都为空),若是则6 7 10 12 else returnLeafNodeCount(T->lchild)+LeafNodeCount(T->rchild); } (2)判别两棵树是否相等。 (3)交换二叉树每个结点的左孩子和右孩子。 voidChangeLR(BiTree&T) { BiTreetemp; if(T->lchild==NULL&&T->rchild==NULL) return; else { temp=T->lchild; T->lchild=T->rchild; T->rchild=temp; } ChangeLR(T->lchild); ChangeLR(T->rchild); } (4)设计二叉树的双序遍历算法(双序遍历是指对于二叉树的每一个结点来说,先访问这个结点,再按双序遍历它的左子树,然后再一次访问这个结点,接下来按双序遍历它的右子树)。

voidDoubleTraverse(BiTreeT) {

if(T==NULL) return;

elseif(T->lchild==NULL&&T->rchild==NULL) cout<data; else { 精心整理

精心整理

cout<data;

DoubleTraverse(T->lchild); cout<data;

DoubleTraverse(T->rchild); } }

(5)计算二叉树最大的宽度(二叉树的最大宽度是指二叉树所有层中结点个数的最大值)。

[题目分析]求二叉树高度的算法见上题。求最大宽度可采用层次遍历的方法,记下各层结点数,每层遍历完毕,若结点数大于原先最大宽度,则修改最大宽度。

intWidth(BiTreebt)//求二叉树bt的最大宽度

{if(bt==null)return(0);//空二叉树宽度为0 else {BiTreeQ[];//Q是队列,元素为二叉树结点指针,容量足够大 front=1;rear=1;last=1;//front队头指针,rear队尾指针,last同层最右结点在队列中的位置 temp=0;maxw=0;//temp记局部宽度,maxw记最大宽度 Q[rear]=bt;//根结点入队列 while(front<=last) {p=Q[front++];temp++;//同层元素数加1 if(p->lchild!=null)Q[++rear]=p->lchild;//左子女入队 if(p->rchild!=null)Q[++rear]=p->rchild;//右子女入队 if(front>last)//一层结束, {last=rear; if(temp>maxw)maxw=temp;//last指向下层最右元素,更新当前最大宽度 temp=0; }//if }//while return(maxw); }//结束width (6)用按层次顺序遍历二叉树的方法,统计树中具有度为1的结点数目。 intLevel(BiTreebt)//层次遍历二叉树,并统计度为1的结点的个数 {intnum=0;//num统计度为1的结点的个数 if(bt){QueueInit(Q);QueueIn(Q,bt);//Q是以二叉树结点指针为元素的队列 while(!QueueEmpty(Q)) {p=QueueOut(Q);printf(p->data);//出队,访问结点 if(p->lchild&&!p->rchild||!p->lchild&&p->rchild)num++;//度为1的结点 if(p->lchild)QueueIn(Q,p->lchild);//非空左子女入队 if(p->rchild)QueueIn(Q,p->rchild);//非空右子女入队 }}//if(bt)

return(num);}//返回度为1的结点的个数

(7)求任意二叉树中第一条最长的路径长度,并输出此路径上各结点的值。

[题目分析]因为后序遍历栈中保留当前结点的祖先的信息,用一变量保存栈的最高栈顶指针,每当退栈时,栈顶指针高于保存最高栈顶指针的值时,则将该栈倒入辅助栈中,辅助栈始终保存最长路径长度上的结点,直至后序遍历完毕,则辅助栈中内容即为所求。

voidLongestPath(BiTreebt)//求二叉树中的第一条最长路径长度

{BiTreep=bt,l[],s[];//l,s是栈,元素是二叉树结点指针,l中保留当前最长路径中的结点 inti,top=0,tag[],longest=0; 精心整理

精心整理

while(p||top>0)

{while(p){s[++top]=p;tag[top]=0;p=p->Lc;}//沿左分枝向下 if(tag[top]==1)//当前结点的右分枝已遍历

{if(!s[top]->Lc&&!s[top]->Rc)//只有到叶子结点时,才查看路径长度

if(top>longest){for(i=1;i<=top;i++)l[i]=s[i];longest=top;top--;}

//保留当前最长路径到l栈,记住最高栈顶指针,退栈 }

elseif(top>0){tag[top]=1;p=s[top].Rc;}//沿右子分枝向下 }//while(p!=null||top>0) }//结束LongestPath

(8)输出二叉树中从每个叶子结点到根结点的路径。

[题目分析]采用先序遍历的递归方法,当找到叶子结点*b时,由于*b叶子结点尚未添加到path中,因此在输出路径时还需输出b->data值。对应的递归算法如下: voidAllPath(BTNode*b,ElemTypepath[],intpathlen) { inti;

if(b!=NULL) {

if(b->lchild==NULL&&b->rchild==NULL)//*b为叶子结点 {

cout<<\到根结点路径:\for(i=pathlen-1;i>=0;i--) cout<

path[pathlen]=b->data;//将当前结点放入路径中 pathlen++;//路径长度增1 AllPath(b->lchild,path,pathlen);//递归扫描左子树 AllPath(b->rchild,path,pathlen);//递归扫描右子树 pathlen--;//恢复环境 } } }

第6章 图

1.选择题

(1)在一个图中,所有顶点的度数之和等于图的边数的()倍。 A.1/2B.1 C.2D.4

(2)在一个有向图中,所有顶点的入度之和等于所有顶点的出度之和的()倍。 A.1/2B.1 C.2D.4

(3)具有n个顶点的有向图最多有()条边。 精心整理

精心整理

A.nB.n(n-1)C.n(n+1)D.n2

(4)n个顶点的连通图用邻接距阵表示时,该距阵至少有()个非零元素。 A.nB.2(n-1)C.n/2D.n2

(5)G是一个非连通无向图,共有28条边,则该图至少有()个顶点。 A.7B.8 C.9D.10

(6)若从无向图的任意一个顶点出发进行一次深度优先搜索可以访问图中所有的顶点,则该图一定是()图。

A.非连通B.连通C.强连通D.有向

(7)下面( )算法适合构造一个稠密图G的最小生成树。 A.Prim算法B.Kruskal算法C.Floyd算法D.Dijkstra算法

(8)用邻接表表示图进行广度优先遍历时,通常借助()来实现算法。 A.栈B.队列C.树D.图 (9)用邻接表表示图进行深度优先遍历时,通常借助()来实现算法。 A.栈B.队列C.树D.图 (10)深度优先遍历类似于二叉树的()。 A.先序遍历B.中序遍历C.后序遍历D.层次遍历 (11)广度优先遍历类似于二叉树的()。 A.先序遍历B.中序遍历C.后序遍历D.层次遍历 (12)图的BFS生成树的树高比DFS生成树的树高()。 A.小B.相等C.小或相等D.大或相等 (13)已知图的邻接矩阵如图6.25所示,则从顶点0出发按深度优先遍历的结果是()。 ?0?1??1??1?1??0??111110011000010010011101?01??00??10?10??01?10??0011000A.0243156 B.0136542 C.0134256 D.0361542 图6.25邻接矩阵 (14)已知图的邻接表如图6.26所示,则从顶点0出发按广度优先遍历的结果是(),按深度优先遍历的结果是()。 A.0132B.0231 C.0321D.0123 图6.26邻接表

(15)下面()方法可以判断出一个有向图是否有环。

A.深度优先遍历B.拓扑排序C.求最短路径D.求关键路径 2.应用题

(1)已知如图6.27所示的有向图,请给出: ①每个顶点的入度和出度; ②邻接矩阵; ③邻接表; ④逆邻接表。 精心整理

精心整理

(2)已知如图6.28所示的无向网,请给出: ①邻接矩阵; ②邻接表; ③最小生成树 a b c d e f g h → b 4 → c 3 → a 4 → c 5 → a 3 → b 5 → b 5 → c 5 → b 9 → d 7 → d 6 → e 3 → d 5 → f 2 → c 5 → d 4 5 5 7 3 2 6 6 → d → d → e → f → g → h → g → e → h → f ^ ^ ^ ^ 9 ^ 5 ^

图6.27有向

图 6.28无向 6 → g 5 → h 4^ (3)已知图的邻接矩阵如6.29所示。试分别画出自顶点1出发进行遍历所得的深度优先生成树和广度优先生成树。 (4)有向网如图6.30所示,试用迪杰斯特拉算法求出从顶点a到其他各顶点间的最短路径,完成表6.9。

图6.30有向网 图6.29邻接矩阵 D 终点 b c d e f g S 终点集 {a,c} {a,c,f} {a,c,f,e} {a,c,f,e,d} {a,c,f,e,d,g} {a,c,f,e,d,g,b} 15 (a,b) 2 (a,c) 12 (a,d) ∞ 15 (a,b) 15 (a,b) 15 (a,b) 15 (a,b) 15 (a,b) i=1 i=2 i=3 i=4 i=5 i=6 12 (a,d) 10 (a,c,e) 6 (a,c,f) ∞ 11 (a,c,f,d) 10 (a,c,e) 11 (a,c,f,d) 16 (a,c,f,g) 14 (a,c,f,d,g) ∞ 16 (a,c,f,g) ∞ (5)试对图6.31所示的AOE-网:

①求这个工程最早可能在什么时间结束; 精心整理

精心整理

②求每个活动的最早开始时间和最迟开始时间; ③确定哪些活动是关键活动

图6.31AOE-网 【解答】按拓扑有序的顺序计算各个顶点的最早可能开始时间Ve和最迟允许开始时间Vl。然后再计算各个

活动的最早可能开始时间e和最迟允许开始时间l,根据l-e=0?来确定关键活动,从而确定关键路径。 Ve Vl <1,2> e l 7 l-e 7 0 11<1,3> 0 5 0 5 0 0 17 8 <3,2> 19 29 0 2 <2,4> 19 17 1<2,5> 15 27 8 <3,5> 19 38 0 <4,6> 28 3<5,6> 30 19 15 37 38 43 1? 0 2? 19 3? 15 4? 29 5? 38 6? 43 此工程最早完成时间为43。关键路径为<1,3><3,2><2,5><5,6> 3.算法设计题 (1)分别以邻接矩阵和邻接表作为存储结构,实现以下图的基本操作: ①增添一个新顶点v,InsertVex(G,v); ②删除顶点v及其相关的边,DeleteVex(G,v); ③增加一条边,InsertArc(G,v,w); ④删除一条边,DeleteArc(G,v,w)。 //本题中的图G均为有向无权图,其余情况容易由此写出 StatusInsert_Vex(MGraph&G,charv)//在邻接矩阵表示的图G上插入顶点v { if(G.vexnum+1)>MAX_VERTEX_NUMreturnINFEASIBLE; G.vexs[++G.vexnum]=v; returnOK; }//Insert_Vex StatusInsert_Arc(MGraph&G,charv,charw)//在邻接矩阵表示的图G上插入边(v,w) { if((i=LocateVex(G,v))<0)returnERROR; if((j=LocateVex(G,w))<0)returnERROR; if(i==j)returnERROR; if(!G.arcs[j].adj) {

G.arcs[j].adj=1; G.arcnum++; }

returnOK; }//Insert_Arc

StatusDelete_Vex(MGraph&G,charv)//在邻接矩阵表示的图G上删除顶点v {

n=G.vexnum; 精心整理

精心整理

if((m=LocateVex(G,v))<0)returnERROR;

G.vexs[m]<->G.vexs[n];//将待删除顶点交换到最后一个顶点 for(i=0;i

G.arcs[m]=G.arcs[n];

G.arcs[m]=G.arcs[n];//将边的关系随之交换 }

G.arcs[m][m].adj=0; G.vexnum--; returnOK; }//Delete_Vex

分析:如果不把待删除顶点交换到最后一个顶点的话,算法将会比较复杂,而伴随着大量元素的移动,时间复杂度也会大大增加. StatusDelete_Arc(MGraph&G,charv,charw)//在邻接矩阵表示的图G上删除边(v,w) {

if((i=LocateVex(G,v))<0)returnERROR; if((j=LocateVex(G,w))<0)returnERROR; if(G.arcs[j].adj) {

G.arcs[j].adj=0; G.arcnum--; }

returnOK; }//Delete_Arc //为节省篇幅,本题只给出Insert_Arc算法.其余算法请自行写出. StatusInsert_Arc(ALGraph&G,charv,charw)//在邻接表表示的图G上插入边(v,w) {

if((i=LocateVex(G,v))<0)returnERROR; if((j=LocateVex(G,w))<0)returnERROR; p=(ArcNode*)malloc(sizeof(ArcNode)); p->adjvex=j;p->nextarc=NULL; else {

if(q->adjvex==j)returnERROR;//边已经存在 q->nextarc=p; }

G.arcnum++; returnOK; }//Insert_Arc

(2)一个连通图采用邻接表作为存储结构,设计一个算法,实现从顶点v出发的深度优先遍历的非递归过程。

数据结构考研指导232页7.3.7

(3)设计一个算法,求图G中距离顶点v的最短路径长度最大的一个顶点,设v可达其余各个顶点。 数据结构考研指导232页7.3.8 精心整理

精心整理

(4)试基于图的深度优先搜索策略写一算法,判别以邻接表方式存储的有向图中是否存在由顶点vi到顶点vj的路径(i≠j)。

解1:

intvisited[MAXSIZE];//指示顶点是否在当前路径上

intexist_path_DFS(ALGraphG,inti,intj)//深度优先判断有向图G中顶点i到顶点j 是否有路径,是则返回1,否则返回0 {

if(i==j)return1;//i就是j else {

visited[i]=1;

for(p=G.vertices[i].firstarc;p;p=p->nextarc) {

k=p->adjvex; if(!visited[k]&&exist_path(k,j))return1;//i下游的顶点到j有路径 }//for }//else }//exist_path_DFS 解2:(以上算法似乎有问题:如果不存在路径,则原程序不能返回0。我的解决方式是在原程序的中引入一变量level来控制递归进行的层数。具体的方法我在程序中用红色标记出来了。) intvisited[MAXSIZE];//指示顶点是否在当前路径上 intlevel=1;//递归进行的层数 intexist_path_DFS(ALGraphG,inti,intj)//深度优先判断有向图G中顶点i到顶点j 是否有路径,是则返回1,否则返回0 {

if(i==j)return1;//i就是j else {

visited[i]=1; for(p=G.vertices[i].firstarc;p;p=p->nextarc,level--) {level++; k=p->adjvex; if(!visited[k]&&exist_path(k,j))return1;//i下游的顶点到j有路径 }//for }//else if(level==1)return0; }//exist_path_DFS

(5)采用邻接表存储结构,编写一个算法,判别无向图中任意给定的两个顶点之间是否存在一条长度为为k的简单路径。

(注1:一条路径为简单路径指的是其顶点序列中不含有重现的顶点。 注2:此题可参见严题集P207-208中有关按“路径”遍历的算法基本框架。) intvisited[MAXSIZE];

intexist_path_len(ALGraphG,inti,intj,intk)//判断邻接表方式存储的有向图G 的顶点i到j是否存在长度为k的简单路径 精心整理

精心整理

{ {

if(i==j&&k==0)return1;//找到了一条路径,且长度符合要求 elseif(k>0) {

visited[i]=1;

for(p=G.vertices[i].firstarc;p;p=p->nextarc) {

l=p->adjvex; if(!visited[l])

if(exist_path_len(G,l,j,k-1))return1;//剩余路径长度减一 }//for visited[i]=0;//本题允许曾经被访问过的结点出现在另一条路径中 }//else return0;//没找到 }//exist_path_len 精心整理

精心整理

第7章查找

1.选择题

(1)对n个元素的表做顺序查找时,若查找每个元素的概率相同,则平均查找长度为()。 A.(n-1)/2B.n/2 C.(n+1)/2D.n

(2)适用于折半查找的表的存储方式及元素排列要求为()。 A.链接方式存储,元素无序B.链接方式存储,元素有序 C.顺序方式存储,元素无序D.顺序方式存储,元素有序

(3)当在一个有序的顺序表上查找一个数据时,既可用折半查找,也可用顺序查找,但前者比后者的查找速度()。 A.必定快B.不一定 C.在大部分情况下要快D.取决于表递增还是递减 (4)折半查找有序表(4,6,10,12,20,30,50,70,88,100)。若查找表中元素58,则它将依次与表中()比较大小,查找结果是失败。 A.20,70,30,50B.30,88,70,50 C.20,50D.30,88,50 (5)对22个记录的有序表作折半查找,当查找失败时,至少需要比较()次关键字。 A.3B.4 C.5D.6 (6)折半搜索与二叉排序树的时间性能()。 A.相同B.完全不同 C.有时不相同D.数量级都是O(log2n) (7)分别以下列序列构造二叉排序树,与用其它三个序列所构造的结果不同的是()。 A.(100,80,90,60,120,110,130) B.(100,120,110,130,80,60,90) C.(100,60,80,90,120,110,130) D.(100,80,60,90,120,130,110) (8)在平衡二叉树中插入一个结点后造成了不平衡,设最低的不平衡结点为A,并已知A的左孩子的平衡因子为0右孩子的平衡因子为1,则应作()型调整以使其平衡。 A.LLB.LRC.RLD.RR (9)下列关于m阶B-树的说法错误的是()。 A.根结点至多有m棵子树 B.所有叶子都在同一层次上 C.非叶结点至少有m/2(m为偶数)或m/2+1(m为奇数)棵子树 D.根结点中的数据是有序的 (10)下面关于B-和B+树的叙述中,不正确的是()。 A.B-树和B+树都是平衡的多叉树B.B-树和B+树都可用于文件的索引结构 C.B-树和B+树都能有效地支持顺序检索D.B-树和B+树都能有效地支持随机检索 (11)m阶B-树是一棵()。

A.m叉排序树B.m叉平衡排序树

C.m-1叉平衡排序树D.m+1叉平衡排序树 (12)下面关于哈希查找的说法,正确的是()。

A.哈希函数构造的越复杂越好,因为这样随机性好,冲突小 B.除留余数法是所有哈希函数中最好的

C.不存在特别好与坏的哈希函数,要视情况而定 D.哈希表的平均查找长度有时也和记录总数有关 (13)下面关于哈希查找的说法,不正确的是()。

A.采用链地址法处理冲突时,查找一个元素的时间是相同的 精心整理

精心整理

B.采用链地址法处理冲突时,若插入规定总是在链首,则插入任一个元素的时间是相同的

C.用链地址法处理冲突,不会引起二次聚集现象 D.用链地址法处理冲突,适合表长不确定的情况

(14)设哈希表长为14,哈希函数是H(key)=key,表中已有数据的关键字为15,38,61,84共四个,现要将关键字为49的元素加到表中,用二次探测法解决冲突,则放入的位置是()。 A.8B.3 C.5D.9

(15)采用线性探测法处理冲突,可能要探测多个位置,在查找成功的情况下,所探测的这些位置上的关键字()。

A.不一定都是同义词B.一定都是同义词 C.一定都不是同义词D.都相同 2.应用题

(1)假定对有序表:(3,4,5,7,24,30,42,54,63,72,87,95)进行折半查找,试回答下列问题: ①画出描述折半查找过程的判定树; ②若查找元素54,需依次与哪些元素比较? ③若查找元素90,需依次与哪些元素比较? ④假定每个元素的查找概率相等,求查找成功时的平均查找长度。 ①先画出判定树如下(注:mid=?(1+12)/2?=6): 30 563 374287 424547295 ②查找元素54,需依次与30,63,42,54元素比较; ③查找元素90,需依次与30,63,87,95元素比较; ④求ASL之前,需要统计每个元素的查找次数。判定树的前3层共查找1+2×2+4×3=17次; 但最后一层未满,不能用8×4,只能用5×4=20次, 所以ASL=1/12(17+20)=37/12≈3.08 (2)在一棵空的二叉排序树中依次插入关键字序列为12,7,17,11,16,2,13,9,21,4,请画出所得到的二叉排序树。 12

7 17 2111621 4913

验算方法:用中序遍历应得到排序结果:2,4,7,9,11,12,13,16,17,21 (3)已知如下所示长度为12的表:(Jan,Feb,Mar,Apr,May,June,July,Aug,Sep,Oct,Nov,Dec) ①试按表中元素的顺序依次插入一棵初始为空的二叉排序树,画出插入完成之后的二叉排序树,并求其在等概率的情况下查找成功的平均查找长度。 ②若对表中元素先进行排序构成有序表,求在等概率的情况下对此有序表进行折半查找时查找成功的平均查找长度。

③按表中元素顺序构造一棵平衡二叉排序树,并求其在等概率的情况下查找成功的平均查找长度。

精心整理

精心整理 解:

(4)对下面的3阶B-树,依次执行下列操作,画出各步操作的结果。

①插入90②插入25③插入45④删除60

(5)设哈希表的地址范围为0~17,哈希函数为:H(key)=key。用线性探测法处理冲突,输入关键字序列:(10,24,32,17,31,30,46,47,40,63,49),构造哈希表,试回答下列问题: ①画出哈希表的示意图; ②若查找关键字63,需要依次与哪些关键字进行比较? ③若查找关键字60,需要依次与哪些关键字比较? ④假定每个关键字的查找概率相等,求查找成功时的平均查找长度。 ①画表如下: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 32 17 63 49 24 40 10 30 31 46 47 ②查找63,首先要与H(63)=63=15号单元内容比较,即63vs31,no; 然后顺移,与46,47,32,17,63相比,一共比较了6次! ③查找60,首先要与H(60)=60=12号单元内容比较,但因为12号单元为空(应当有空标记),所以应当只比较这一次即可。 ④对于黑色数据元素,各比较1次;共6次; 对红色元素则各不相同,要统计移位的位数。“63”需要6次,“49”需要3次,“40”需要2次,“46”需要3次,“47”需要3次, 所以ASL=1/11(6+2+3×3+6)=23/11 (6)设有一组关键字(9,01,23,14,55,20,84,27),采用哈希函数:H(key)=key%7,表长为10,用开放地址法的二次探测法处理冲突。要求:对该关键字序列构造哈希表,并计算查找成功的平均查找长度。

散列地址 0 1 2 3 4 5 6 7 8 9 关键字 14 01 1 9 1 23 2 84 3 27 4 55 1 20 2 比较次数 1 平均查找长度:ASLsucc=(1+1+1+2+3+4+1+2)/8=15/8

以关键字27为例:H(27)=27%7=6(冲突)H1=(6+1)=7(冲突)

23

H2=(6+2)=0(冲突)H3=(6+3)=5所以比较了4次。

(7)设哈希函数H(K)=3Kmod11,哈希地址空间为0~10,对关键字序列(32,13,49,24,38,21,4,12),按下述两种解决冲突的方法构造哈希表,并分别求出等概率下查找成功时和查找失败时的平均查找长度ASLsucc和ASLunsucc。

①线性探测法; 精心整理

精心整理

②链地址法。 ① 散列地址 0 关键字 比较次数 1 4 1 2 3 12 1 4 49 1 5 38 2 6 13 1 7 24 2 8 32 1 9 21 2 10 ASLsucc=(1+1+1+2+1+2+1+2)/8=11/8

ASLunsucc=(1+2+1+8+7+6+5+4+3+2+1)/11=40/11 ②

ASLsucc=(1*5+2*3)/8=11/8

ASLunsucc=(1+2+1+2+3+1+3+1+3+1+1)/11=19/11

(5)设哈希表的地址范围为0~17,哈希函数为:H(key)=key。用线性探测法处理冲突,输入关键字序列:(10,24,32,17,31,30,46,47,40,63,49),构造哈希表,试回答下列问题: ①画出哈希表的示意图; ②若查找关键字63,需要依次与哪些关键字进行比较? ③若查找关键字60,需要依次与哪些关键字比较? ④假定每个关键字的查找概率相等,求查找成功时的平均查找长度。 解:(1)画表如下: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 32 17 63 49 24 40 10 30 31 46 47 (2)查找63,首先要与H(63)=63=15号单元内容比较,即63vs31,no; 然后顺移,与46,47,32,17,63相比,一共比较了6次! (3)查找60,首先要与H(60)=60=12号单元内容比较,但因为12号单元为空(应当有空标记),所以应当只比较这一次即可。 (4)对于黑色数据元素,各比较1次;共6次; 对红色元素则各不相同,要统计移位的位数。“63”需要6次,“49”需要3次,“40”需要2次,“46”需要3次,“47”需要3次, 所以ASL=1/11(6+2+3×3+6)=23/11 (6)设有一组关键字(9,01,23,14,55,20,84,27),采用哈希函数:H(key)=key%7,表长为10,用开放地址法的二次探测法处理冲突。要求:对该关键字序列构造哈希表,并计算查找成功的平均查找长度。

散列地址 0 1 2 3 4 5 6 7 8 9 关键字 14 01 1 9 1 23 2 84 3 27 4 55 1 20 2 比较次数 1 平均查找长度:ASLsucc=(1+1+1+2+3+4+1+2)/8=15/8 以关键字27为例:H(27)=27%7=6(冲突)H1=(6+1)=7(冲突) 23H2=(6+2)=0(冲突)H3=(6+3)=5所以比较了4次。 (7)设哈希函数H(K)=3Kmod11,哈希地址空间为0~10,对关键字序列(32,13,49,24,38,21,4,12),按下述两种解决冲突的方法构造哈希表,并分别求出等概率下查找成功时和查找失败时的平均查找长度ASLsucc和ASLunsucc。

①线性探测法; ②链地址法。 散列地址 0 1 2 3 4 5 6 7 8 9 10 关键字 4 1 12 1 49 1 38 2 13 1 24 2 32 1 21 2 比较次数 ASLsucc=(1+1+1+2+1+2+1+2)/8=11/8 ASLunsucc=(1+2+1+8+7+6+5+4+3+2+1)/11=40/11 精心整理

精心整理

第8章排序

1.选择题

(1)从未排序序列中依次取出元素与已排序序列中的元素进行比较,将其放入已排序序列的正确位置上的方法,这种排序方法称为()。

A.归并排序B.冒泡排序C.插入排序D.选择排序

(2)从未排序序列中挑选元素,并将其依次放入已排序序列(初始时为空)的一端的方法,称为()。 A.归并排序B.冒泡排序C.插入排序D.选择排序

(3)对n个不同的关键字由小到大进行冒泡排序,在下列()情况下比较的次数最多。 A.从小到大排列好的B.从大到小排列好的 C.元素无序D.元素基本有序 (4)对n个不同的排序码进行冒泡排序,在元素无序的情况下比较的次数最多为()。 A.n+1B.nC.n-1D.n(n-1)/2 (5)快速排序在下列()情况下最易发挥其长处。 A.被排序的数据中含有多个相同排序码 B.被排序的数据已基本有序 C.被排序的数据完全无序 D.被排序的数据中的最大值和最小值相差悬殊 (6)对n个关键字作快速排序,在最坏情况下,算法的时间复杂度是()。 A.O(n)B.O(n2)C.O(nlog2n)D.O(n3) (7)若一组记录的排序码为(46,79,56,38,40,84),则利用快速排序的方法,以第一个记录为基准得到的一次划分结果为()。 A.38,40,46,56,79,84B.40,38,46,79,56,84 C.40,38,46,56,79,84D.40,38,46,84,56,79 (8)下列关键字序列中,()是堆。 A.16,72,31,23,94,53B.94,23,31,72,16,53 C.16,53,23,94,31,72D.16,23,53,31,94,72 (9)堆是一种()排序。 A.插入B.选择C.交换D.归并 (10)堆的形状是一棵()。 A.二叉排序树B.满二叉树C.完全二叉树D.平衡二叉树 (11)若一组记录的排序码为(46,79,56,38,40,84),则利用堆排序的方法建立的初始堆为()。 A.79,46,56,38,40,84B.84,79,56,38,40,46 C.84,79,56,46,40,38D.84,56,79,40,46,38 (12)下述几种排序方法中,要求内存最大的是()。 A.希尔排序B.快速排序C.归并排序D.堆排序 (13)下述几种排序方法中,()是稳定的排序方法。 A.希尔排序B.快速排序C.归并排序D.堆排序

(14)数据表中有10000个元素,如果仅要求求出其中最大的10个元素,则采用()算法最节省时间。 A.冒泡排序B.快速排序C.简单选择排序D.堆排序 (15)下列排序算法中,()不能保证每趟排序至少能将一个元素放到其最终的位置上。 A.希尔排序B.快速排序C.冒泡排序D.堆排序 2.应用题

(1)设待排序的关键字序列为{12,2,16,30,28,10,16*,20,6,18},试分别写出使用以下排序方法,精心整理

精心整理

每趟排序结束后关键字序列的状态。

①直接插入排序 ②折半插入排序

③希尔排序(增量选取5,3,1) ④冒泡排序 ⑤快速排序 ⑥简单选择排序 ⑦堆排序

⑧二路归并排序

①直接插入排序

[212]1630281016*20618 [21216]30281016*20618 [2121630]281016*20618 [212162830]1016*20618 [21012162830]16*20618 [210121616*2830]20618 [210121616*202830]618 [2610121616*202830]18 [2610121616*18202830] ②折半插入排序排序过程同① ③希尔排序(增量选取5,3,1) 102166181216*203028(增量选取5) 621210181616*203028(增量选取3) 2610121616*18202830(增量选取1) ④冒泡排序 21216281016*20618[30] 212161016*20618[2830] 212101616*618[202830] 2101216616*[18202830] 21012616[16*18202830] 210612[1616*18202830] 2610[121616*18202830] 2610121616*18202830] ⑤快速排序 12[6210]12[283016*201618] 6[2]6[10]12[283016*201618] 28261012[181616*20]28[30] 18261012[16*16]18[20]2830 16*26101216*[16]18202830

左子序列递归深度为1,右子序列递归深度为3 ⑥简单选择排序

2[121630281016*20618] 26[1630281016*201218] 2610[30281616*201218] 261012[281616*203018] 26101216[2816*203018] 精心整理

精心整理

2610121616*[28203018] 2610121616*18[203028] 2610121616*1820[2830] 2610121616*182028[30] ⑧二路归并排序

2121630102816*20618 21216301016*2028618 210121616*202830618 2610121616*18202830 ⑦堆排序

第一步,形成初始大根堆(详细过程略),第二步做堆排序。

12 2 30 20 6 18 28 10 16 16* 2 20 6 12 28 18 30 16 10 16*

初始排序不是大根堆形成初始大根堆12 28 20 2 6 30 18 10 16 16* 2 12 6 30 20 18 28 16 10 16*

交换1与10对象从1到9重新形成堆 6 20 12 2 28 30 18 10 16 16* 2 12 28 30 18 6 20 16 10 16* 交换1与9对象从1到8重新形成堆

精心整理

精心整理

2 18 16 12 6 10 16* 20 28 30 交换1与8对象从1到7重新形成堆

16* 12 16 2 6 10 18 20 28 30 交换1与7对象从1到6重新形成堆 10 12 16 2 6 16* 18 20 28 30 交换1与6对象从1到5重新形成堆 6 12 10 2 16 16* 18 20 28 30 交换1与5对象从1到4重新形成堆

精心整理

18 12 16 2 12 10 16* 20 28 30

16* 12 16 2 6 10 18 20 28 30 16 12 10 2 6 16* 18 20 28 30 12 6 10 2 16 16* 18 20 28 30 精心整理

12 6 12 20 28 30 16 16* 10 18 20 12 28 30 6 16 10 2 16* 18

交换1与4对象从1到3重新形成堆

2 6 12 20 28 30 16 16* 10 18 20 12 28 30 2 16 6 10 16* 18 交换1与3对象从1到2重新形成堆 2 6 12 20 28 30 16 16* 10 18 20 12 28 30 6 16 2 10 16* 18

交换1与2对象得到结果 (2)给出如下关键字序列{321,156,57,46,28,7,331,33,34,63},试按链式基数排序方法,列出每一趟分配和收集的过程。 3.算法设计题 (1)试以单链表为存储结构,实现简单选择排序算法。 voidLinkedListSelectSort(LinkedListhead)

//本算法一趟找出一个关键字最小的结点,其数据和当前结点进行交换;若要交换指针,则须记下 //当前结点和最小结点的前驱指针 p=head->next; while(p!=null)

{q=p->next;r=p;//设r是指向关键字最小的结点的指针 while(q!=null)

{if(q->datadata)r=q; q:=q->next; 精心整理

精心整理 }

if(r!=p)r->data<-->p->data; p=p->next; }

(2)有n个记录存储在带头结点的双向链表中,现用双向冒泡排序法对其按上升序进行排序,请写出这种排序的算法。(注:双向冒泡排序即相邻两趟排序向相反方向冒泡)。 typedefstructnode {ElemTypedata;

structnode*prior,*next; }node,*DLinkedList;

voidTwoWayBubbleSort(DLinkedListla)

//对存储在带头结点的双向链表la中的元素进行双向起泡排序。 {intexchange=1;//设标记 DLinkedListp,temp,tail; head=la//双向链表头,算法过程中是向下起泡的开始结点 tail=null;//双向链表尾,算法过程中是向上起泡的开始结点 while(exchange) {p=head->next;//p是工作指针,指向当前结点 exchange=0;//假定本趟无交换 while(p->next!=tail)//向下(右)起泡,一趟有一最大元素沉底 if(p->data>p->next->data)//交换两结点指针,涉及6条链 {temp=p->next;exchange=1;//有交换 p->next=temp->next;temp->next->prior=p//先将结点从链表上摘下 temp->next=p;p->prior->next=temp;//将temp插到p结点前 temp->prior=p->prior;p->prior=temp; } elsep=p->next;//无交换,指针后移 tail=p;//准备向上起泡 p=tail->prior; while(exchange&&p->prior!=head)//向上(左)起泡,一趟有一最小元素冒出 if(p->dataprior->data)//交换两结点指针,涉及6条链 {temp=p->prior;exchange=1;//有交换 p->prior=temp->prior;temp->prior->next=p;//先将temp结点从链表上摘下 temp->prior=p;p->next->prior=temp;//将temp插到p结点后(右) temp->next=p->next;p->next=temp; } elsep=p->prior;//无交换,指针前移 head=p;//准备向下起泡

}//while(exchange) }//算法结束

(3)设有顺序放置的n个桶,每个桶中装有一粒砾石,每粒砾石的颜色是红,白,蓝之一。要求重新安排这些砾石,使得所有红色砾石在前,所有白色砾石居中,所有蓝色砾石居后,重新安排时对每粒砾石的颜色只能看一次,并且只允许交换操作来调整砾石的位置。

[题目分析]利用快速排序思想解决。由于要求“对每粒砾石的颜色只能看一次”,设3个指针i,j和k,分别指向红色、白色砾石的后一位置和待处理的当前元素。从k=n开始,从右向左搜索,若该元素是兰色,则元素不动,指针左移(即k-1);若当前元素是红色砾石,分i>=j(这时尚没有白色砾石)和i

精心整理

况执行第i个元素和第k个元素交换,之后i+1;后一情况,i所指的元素已处理过(白色),j所指的元素尚未处理,应先将i和j所指元素交换,再将i和k所指元素交换。对当前元素是白色砾石的情况,也可类似处理。

为方便处理,将三种砾石的颜色用整数1、2和3表示。 voidQkSort(rectyper[],intn)

//r为含有n个元素的线性表,元素是具有红、白和兰色的砾石,用顺序存储结构存储, //本算法对其排序,使所有红色砾石在前,白色居中,兰色在最后。 {inti=1,j=1,k=n,temp; while(k!=j)

{while(r[k].key==3)k--;//当前元素是兰色砾石,指针左移 if(r[k].key==1)//当前元素是红色砾石

if(i>=j){temp=r[k];r[k]=r[i];r[i]=temp;i++;}

//左侧只有红色砾石,交换r[k]和r[i] else{temp=r[j];r[j]=r[i];r[i]=temp;j++; //左侧已有红色和白色砾石,先交换白色砾石到位 temp=r[k];r[k]=r[i];r[i]=temp;i++; //白色砾石(i所指)和待定砾石(j所指) }//再交换r[k]和r[i],使红色砾石入位。 if(r[k].key==2) if(i<=j){temp=r[k];r[k]=r[j];r[j]=temp;j++;} //左侧已有白色砾石,交换r[k]和r[j] else{temp=r[k];r[k]=r[i];r[i]=temp;j=i+1;} //i、j分别指向红、白色砾石的后一位置 }//while if(r[k]==2)j++;/*处理最后一粒砾石 elseif(r[k]==1){temp=r[j];r[j]=r[i];r[i]=temp;i++;j++;} //最后红、白、兰色砾石的个数分别为:i-1;j-i;n-j+1 }//结束QkSor算法 [算法讨论]若将j(上面指向白色)看作工作指针,将r[1..j-1]作为红色,r[j..k-1]为白色,r[k..n]为兰色。从j=1开始查看,若r[j]为白色,则j=j+1;若r[j]为红色,则交换r[j]与r[i],且j=j+1,i=i+1;若r[j]为兰色,则交换r[j]与r[k];k=k-1。算法进行到j>k为止。 算法片段如下: inti=1,j=1,k=n; while(j<=k) if(r[j]==1)//当前元素是红色 {temp=r[i];r[i]=r[j];r[j]=temp;i++;j++;} elseif(r[j]==2)j++;//当前元素是白色 else//(r[j]==3当前元素是兰色

{temp=r[j];r[j]=r[k];r[k]=temp;k--;}

对比两种算法,可以看出,正确选择变量(指针)的重要性。

(4)编写算法,对n个关键字取整数值的记录序列进行整理,以使所有关键字为负值的记录排在关键字为非负值的记录之前,要求:

①采用顺序存储结构,至多使用一个记录的辅助存储空间; ②算法的时间复杂度为O(n)。

(5)借助于快速排序的算法思想,在一组无序的记录中查找给定关键字值等于key的记录。设此组记录存放于数组r[l..n]中。若查找成功,则输出该记录在r数组中的位置及其值,否则显示“notfind”信息。请简要说明算法思想并编写算法。 精心整理

精心整理

[题目分析]把待查记录看作枢轴,先由后向前依次比较,若小于枢轴,则从前向后,直到查找成功返回其位置或失败返回0为止。

intindex(RecTypeR[],intl,h,datatypekey) {inti=l,j=h; while(i

{while(i<=j&&R[j].key>key)j--; if(R[j].key==key)returnj; while(i<=j&&R[i].key

printf(“Notfind”);return0; }//index (6)有一种简单的排序算法,叫做计数排序。这种排序算法对一个待排序的表进行排序,并将排序结果存放到另一个新的表中。必须注意的是,表中所有待排序的关键字互不相同,计数排序算法针对表中的每个记录,扫描待排序的表一趟,统计表中有多少个记录的关键字比该记录的关键字小。假设针对某一个记录,统计出的计数值为c,那么,这个记录在新的有序表中的合适的存放位置即为c。 ①给出适用于计数排序的顺序表定义; ②编写实现计数排序的算法; ③对于有n个记录的表,关键字比较次数是多少? ④与简单选择排序相比较,这种方法是否更好?为什么? typedefstruct {intkey;datatypeinfo}RecType voidCountSort(RecTypea[],b[],intn)//计数排序算法,将a中记录排序放入b中 {for(i=0;i

if(a[i].key

精心整理