《数据结构C语言版》习题指导与解答.docx
《《数据结构C语言版》习题指导与解答.docx》由会员分享,可在线阅读,更多相关《《数据结构C语言版》习题指导与解答.docx(32页珍藏版)》请在冰豆网上搜索。
《数据结构C语言版》习题指导与解答
附录2习题指导与解答
习题一解答
1.数据是人们利用文字符号、数字符号以及其他规定的符号对客观现实世界的事物及其活动所做的抽象描述。
它是计算机程序加工的“原料”。
表示一个事物的一组数据称为一个数据元素,它是数据的基本单位,在计算机中通常作为一个整体来进行考虑和处理。
一般情况下,一个数据元素由若干个数据项构成。
数据对象是性质相同的数据元素的集合,是数据的一个子集。
例如:
描述N个学生的有关信息的N个数据元素构成了一个数据对象。
2.数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
具体来说,数据结构包含三个方面的内容,既数据的逻辑结构、数据的存储结构(或称物理结构)和对数据所施加的一组操作。
3.数据的逻辑结构是数据元素之间本身所固有的独立于计算机的一种结构,这种结构可以用数据元素之间固有的关系的集合来描述。
数据的存储结构(或物理结构)是逻辑结构在计算机存储器中的具体存放方式的体现,是逻辑结构在计算机存储器中的映像。
4.根据数据元素之间存在的关系的不同特性,数据结构通常可以分为如下4类基本结构:
(1)线性结构。
元素之间存在一个一对一的线线关系,即除了第一个元素和最后一个元素外,每个元素都有一个直接前驱和一个直接后继,第一个元素有一个后继,最后一个元素有一个直接前驱。
例如学生档案管理系统中学生记录之间的关系即为线性关系;
(2)树形结构。
数据元素之间存在着一个对多个的关系。
例如,老师T指导3个硕士研究生G1,G2,G3;每个研究生Gi(i=1,2,3)又分别指导3个本科生Si1,Si2,Si3;则数据元素之间的呈现树形结构。
(3)图形结构或网状结构。
数据元素之间存在多个对多个的关系。
如城市交通网络中城市之间的交通道路的连接关系就是一个网状结构。
(4)集合结构。
数据元素之间无任何关系。
5.抽象数据类型通常是指由用户定义,用以表示实际应用问题的数据模型,一般由基本数据类型或其他已定义的抽象数据类型以及定义在该模型上的一组操作组成。
在C或C++语言中,一般可用struc或直接用“类”来定义抽象数据类型。
6.算法(Algorithm)是对待定问题求解步骤的一种描述,它是指令的有限序列,其中每一条指令表示一个或多个操作。
算法分析(Algorithmanalysis)的主要工作是从“时间”和“空间”两个方面来分析算法的效率。
7.算法应具有如下5个重要特性:
(1)输入性;
(2)输出性;(3)有限性;(4)确定性;(5)可行性;
算法设计应满足以下5个基本要求:
(1)正确性;
(2)可读性;(3)健壮性;(4)高时间效率;(5)高空间效率。
8.
(1)当n=10的时候有210>103,而29<93,故当n≥10时,有2n>n3。
(2)O(n)。
(3)因为当n趋向于无穷大时有lim((2n+n3)/2n)=1,2n+n3的同阶数量级是O(2n)。
第2大题略
习题二解答
1.填空
⑴表长的一半,表长,该元素在表中的位置
⑵144第5个元素的存储地址=第1个元素的存储地址+(7-1)×4=144
⑶p->next=(p->next)->next
⑷为了运算方便
⑸Ο
(1),Ο(n)在p所指结点后插入一个新结点只需修改指针,所以时间复杂度为Ο
(1);而在给定值为x的结点后插入一个新结点需要先查找值为x的结点,所以时间复杂度为Ο(n)。
(6)两个,直接后继,直接前驱,尾结点,头结点.
(7)顺序
2.选择题
⑴A顺序存储的特点是“逻辑上相邻,物理上也相邻”,所以无需存储元素间的关系,故存储密度大。
⑵D线性表的链接存储是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以连续,也可以不连续,甚至可以零散分布在内存中任意位置。
⑶C因为有prior指针
⑷A因为单链表属于顺序存储结构
⑸B
⑹B
(7)B
(8)B
(9)B
(10)BLoc(a6)=Loc(a1)+(6-1)*5=90+10=100
3.简答题
(1)顺序结构存储时,相邻数据元素的存放地址也相邻,即逻辑结构和存储结构是统一的,,要求内存中存储单元的地址必须是连续的。
优点:
一般情况下,存储密度大,存储空间利用率高。
缺点:
a.在做插入和删除操作时,需移动大量元素;b.由于难以估计,必须预先分配较大的空间,往往使存储空间不能得到充分利用;c.表的容量难以扩充。
链式结构存储时,相邻数据元素可随意存放,所占空间分为两部分,一部分存放结点值,另一部分存放表示结点间关系的指针。
优点:
插入和删除元素时很方便,使用灵活。
缺点:
存储密度小,存储空间利用率低。
(2)带头结点的单链表和不带头结点的单链表的区别主要体现在其结构上和算法操作上。
在结构上,带头结点的单链表,不管链表是否为空,均含有一个头结点,不带头结点的单链表不含头结点。
在操作上,带头结点的单链表的初始化为申请一个头结点。
无论插入或删除的位置是地第一个结点还是其他结点,算法步骤都相同。
不带头结点的单链表,其算法步骤要分别考虑插入或删除的位置是第一个结点还是其他结点。
因为两种情况的算法步骤不同。
4.算法设计
⑴从数组的两端向中间比较,设置两个变量i和j,初始时i=0,j=n-1,若A[i]为偶数并且A[j]为奇数,则将A[i]与A[j]交换。
具体算法如下:
voidadjust(intA[],n)
{i=0;j=n-1;
while(i{while(A[i]%2!
=0)i++;
while(A[j]%2=0)j--;
if(i}
}
分析算法,两层循环将数组扫描一遍,所以,时间复杂度为O(n)。
⑵顺序表的逆置,即是将对称元素交换,设顺序表的长度为n,则将表中第i个元素与第n-i-1个元素相交换。
具体算法如下:
voidreverse(elemtyped[],intn)
{for(i=0;i<=n/2;i++)
{t=d[i];
d[i]=d[n-i-1];
d[n-i-1]=t;
}
}
⑶在单链表A中依次取元素,若取出的元素是字母,把它插入到字母链表C中,若取出的元素是数字,则把它插入到数字链表D中,直到链表的尾部,这样表C,D,A中分别存放字母、数字和其他字符。
具体算法如下:
voiddivide(LinklistA,LinklistC,LinklistD)
{C=(Linklist)malloc(sizeof(Lnode));C->next=C;//创建空循环链表C存放字符
D=(Linklist)malloc(sizeof(Lnode));D->next=D;//创建空循环链表D存放数字
p=A;q=p->next;
while(q)
{if((q->data>=’A’)&&(q->data<=’Z’)||(q->data>=’a’)&&(q->data<=’z’))
{p->next=q->next;q->next=C->next;C->next=q;}
elseif((q->data>=’0’)&&(q->data<=’9’))
{p->next=q->next;q->next=D->next;D->next=q;}
elsep=q;
q=p->next;
}
p->next=A;R=A;
}
⑷从头到尾扫描单链表,若当前结点的元素值与后继结点的元素值不相等,则指针后移;否则删除该后继结点。
具体算法如下:
voiddel(LinklistA)
{p=A->next;
while(p->next)
if(p->data==p->next->data)
{q=p->next;p->next=q->next;deleteq;}
elsep=p->next;
}
⑸因为是在有序单链表上的操作,所以,要充分利用其有序性。
在单链表中查找第一个大于mink的结点和第一个小于maxk的结点,再将二者间的所有结点删除。
voiddelbtween(LinklistA,intmink,intmaxk)
{p=A;
while(p->next&&p->next->data<=mink)p=p->next;
if(p->next)
{q=p->next;
while(q->data{x=q->next;p->next=q->next;deleteq;q=x;}
}
}
习题三解答
1.填空
⑴23,1003H
⑵顺序存储结构和链接存储结构(或顺序栈和链栈),栈顶指针top=-1和top=NULL,栈顶指针top等于数组的长度和内存无可用空间
⑶后进先出,先进先出,对插入和删除操作限定的位置不同
⑷假溢出
⑸O
(1),O(n)在带头指针的循环链表中,出队即是删除开始结点,这只需修改相应指针;入队即是在终端结点的后面插入一个结点,这需要从头指针开始查找终端结点的地址。
2.选择题
⑴D此时,输出序列一定是输入序列的逆序。
⑵C解题技巧:
在输出序列中任意元素后面不能出现比该元素小并且是升序(指的是元素的序号)的两个元素。
⑶B每个右括号与它前面的最后一个没有匹配的左括号配对,因此具有后进先出性。
⑷C栈和队列的逻辑结构都是线性的,都有顺序存储和链接存储,有可能包含的运算不一样,但不是主要区别,任何数据结构在针对具体问题时包含的运算都可能不同。
⑸A两栈共享空间首先两个栈是相向增长的,栈底应该分别指向两个栈中的第一个元素的位置,并注意C++中的数组下标是从0开始的。
(6)C队列长度为(rear-front+20)mod20=15
(7)B
(8)C
(9)A递归算法里会用到递归工作栈
(10)B先进先出的特性
3.问答题
(1)栈是一种先进后出的线性表,栈的插入和删除操作都只能在栈顶进行,队列是一种先进先出的线性表,队列的插入只能在队尾进行,队列的删除只能在队头进行,而一般的线性表可以在线性表的任何位置进行插入和删除操作。
(2)因为链栈只在链头插入和删除结点,不可能在链表中间插入和删除结点,算法实现很简单,所以一般不设置头结点。
4.算法设计
(1)出队操作是在循环链表的头部进行,相当于删除开始结点,而入队操作是在循环链表的尾部进行,相当于在终端结点之后插入一个结点。
由于循环链表不带头结点,需要处理空表的特殊情况。
入队算法如下:
voidEnqueue(*rear,Elemtypex)
{s=new;
S->data=x;
if(rear=NULL){rear=s;rear->next=s;}
else{s->next=rear->next;rear->=s;rear=s;}
}
出队算法如下:
ElemtypeDequeue(*rear)
{if(rear==NULL)throw”underflow”;
else{s=rear->next;
if(s==rear)resr=NULL;
elserear->next=s->next;
free(s);
}
}
(2)算法思想:
利用栈和队列的操作特点,可以先将队列中所有元素出队并入栈,然后再将栈中所有元素出栈并入队即可。
voidInverse(StackS,QueueQ)
{while(!
EmptyQueue(Q))
{a=DeQueue(Q);Push(S,a);
}
while(!
EmptyStack(S))
{a=Pop(S);EnQueue(Q,a);
}
}
习题四解答
1.填空
⑴数据元素的类型是一个字符
⑵长度相同且对应位置的字符相等例如"abc"≠"abc","abc"≠"bca"。
⑶14
⑷模式匹配
(5)由空格字符(ASCII值32)所组成的字符串,空格个数
(6)该串所包含的的字符的个数
(7)a.21b.1c.DataBaseStructureCourse
d.DataCoursee.Structuref.6g.DataBaseCourse
2.简答题
(1)不含任何字符的串称为空串,其长度为0。
仅含有空格字符的串称为空格串,其长度为串中空格字符的个数。
空格符可用来分割一般的字符,便于人们识别和阅读,但计算串长时应包括这些空格符。
空串在串处理中可作为任意串的子串。
用引号(数据结构教学中通常用单引号,而C语言中用双引号)括起来的字符序列称为串常量,其串值是常量。
串值可以变化的量称为串变量。
串中任意个连续的字符组成的子序列被称为该串的子串。
包含子串的串又被称为该子串的主串。
子串在主串中第一次出现时的第一个字符的位置称子串在主串中的位置。
串变量与其它变量一样,要用名字引用其值,串变量的名字也是标识符,串变量的值可以修改。
(2)
concat(replace(S1,substr(S1,length(S2),length(S3)),S3),substr(S4,index(S2,‘8’),length(S2)))
=concat(replace(S1,substr(S1,4,3),S3),substr(S4,2,4))
=concat(replace(S1,’DEF’,S3),’1234’)
=concat(‘ABC###G’,’1234’)
=‘ABC###G1234’
(3)
j
1
2
3
4
5
6
7
8
9
10
11
12
串
a
b
a
b
a
a
a
b
a
b
a
a
next[j]
0
1
1
2
3
4
2
2
3
4
5
6
3.算法设计
⑴先判断串S中要删除的内容是否存在,若存在,则将第i+j-1之后的字符前移j个位置。
voidDele(chars[],inti,intj)/*数组s的0号单元存放串的长度*/
{if(i+j
for(k=i;k
s[0]=s[0]-j;
}
elseprintf(“参数非法”);
}
⑵从后向前删除值为ch的所有元素,这样所有移动的元素中没有值为ch的元素,能减少移动元素的次数,提高算法的效率。
算法如下:
voidDele(chars[],charch)/*数组s的0号单元存放串的长度*/
{for(i=s[0];i>0;i--)
if(s[i]==ch){
for(j=i+1;j<=s[0];j++)
s[j-1]=s[j];
s[0]--;
}
}
(3)两个指针,一个从头开始,一个从为开始依次交换直至开头小于结尾时候停止。
算法如下:
#include
#include
voidmain()
{intstrbegin,strend;
stringstr;
chartmp;
charname[100]="abcdeabc";
str=name;
printf(“%s\n”,str);
strbegin=0;
strstrend=str.size()-1;
while(strbegin{tmp=str[strbegin];
str[strbegin]=str[strend];
str[strend]=tmp;
strbegin++;
strend--;
}
printf(“%s\n”,str);
return;
}
(4)在一个字符串内,统计含多少整数的问题,核心是如何将数从字符串中分离出来。
从左到右扫描字符串,初次碰到数字字符时,作为一个整数的开始。
然后进行拼数,即将连续出现的数字字符拼成一个整数,直到碰到非数字字符为止,一个整数拼完,存入数组,再准备下一整数,如此下去,直至整个字符串扫描到结束。
算法如下:
intCountInt()
/*从键盘输入字符串,连续的数字字符算作一个整数,统计其中整数的个数*/
{charch;inti=0,a[];/*整数存储到数组a,i记整数个数*/
scanf(“%c”,&ch);/*从左到右读入字符串*/
while(ch!
=‘#’)/*‘#’是字符串结束标记*/
if(ch>=’0’&&ch<=’9’)/*是数字字符*/
{num=0;/*数初始化*/
while(ch>=’0’&&ch<=’9’)/*拼数*/
{num=num*10+‘ch’-‘0’;
scanf(“%c”,&ch);
}
a[i++]=num;
if(ch!
=‘#’)/*若拼数中输入了‘#’,则不再输入*/
scanf(“%c”,&ch);
}
elsescanf(“%c”,&ch);/*输入非数字且非#时,继续输入字符*/
printf(“共有%d个整数,它们是:
\n”,i);
for(j=0;j
{printf(“%6d”,a[j]);
if((j+1)%10==0)printf(“\n”);}/*每10个数输出在一行上*/
}
[算法讨论]假定字符串中的数均不超过32767,否则,需用长整型数组及变量。
(5)本题是模式匹配问题。
将文本行看作主串,先用子串定位函数确定子串在主串中的位置,如子串在主串中,则计数,并将子串后的部分主串当作新主串,继续查子串是否在主串中,如此下去,直到子串不在主串中为止。
下面用串的基本操作编写求解本题的算法。
并给出子串定位函数Index。
算法如下:
intIndex(StringS,StringT)
{/*子串定位函数,成功返回子串T的第一字符在S中的位置,否则返回0*/
n=StringLength(S);m=StringLength(T);
i=1;
while(i<=n-m+1)/*当i加上T的长度超过串S的长度时结束*/
{StringAssign(sub,SubString(S,i,m));/*取字串sub*/
if(StringEqual(sub,T)!
=0)i++;
elsereturni;
}
return0;/*S中不存在与T相等的子串*/
}
intNumberofSub(Stringmain,Stringsub)
{/*某一个不包含空格的子串在文本行中出现的次数*/
inti=0,j;
intn=StringLength(main),m=StringLength(sub);
j=Index(main,sub);
while(j!
=0)
{i++;
StringAssign(main,SubString(main,j+m,n-(j+m)+1);/*新主串*/
n=StringLength(main);/*求新主串长度*/
j=Index(main,sub);
}
returni;
}
(6)略
习题五解答
1.填空题
⑴对称,三角,对角
⑵k=i*(i-1)/2+j-1
⑶n
⑷(n+1)*n/2
⑸(n+1)*n/2
⑹稀疏
⑺行号,列号,值
⑻n
⑼A=⑧B=③C=⑤D=①E=⑥
(a,b)
b
(c,d)
(d)
2.选择题
⑴A
⑵Dloc(a[2][2])=loc(a[0][0])+((2-0)*4+(2-0))*2=1020
⑶D
⑷A
⑸C
⑹C
⑺A
⑻A
⑼D
D
习题六解答
1.填空题
2.⑴度,孩子,双亲
⑵2h-1,2h-1最小结点个数的情况是第1层有1个结点,其他层上都只有2个结点。
⑶2i-1,(n+1)/2,(n-1)/2设满二叉树中叶子结点的个数为n0,度为2的结点个数为n2,由于满二叉树中不存在度为1的结点,所以n=n0+n2;由二叉树的性质n0=n2+1,得n0=(n+1)/2,n2=(n-1)/2。
⑷64128个结点的完全二叉树中最后一个结点的编号为128,其双亲即最后一个分支结点的编号为64,也就是说,从编号65开始均为叶子。
⑸21根据二叉树性质3的证明过程,有n0=n2+2n3+3n4+1(n0、n2、n3、n4分别为叶子结点、度为2、度为3和度为4的结点的个数)。
⑹FGDBHECA根据前序遍历序列和中序遍历序列将该二叉树构造出来。
⑺3n,n-1,n-1,n+2
⑻n-1,2n-1n-1个分支结点是经过n-1次合并后得到的。
⑼n-1
n-1因为根结点没有双亲
(11)完全二叉树,斜树
(12)队列
2.选择题
⑴D结点B一共有5个孩子
⑵B此题注意是序列正好相反,则左斜树和右斜树均满足条件。
⑶D叶子结点没有左右孩子,所以它的左右孩子域均是线索
⑷D满二叉树中没有度为1的结点,所以有m个叶子结点,则根据性质3可知度为2的结点个数为m-1。
⑸A三种遍历次序均是先左子树后右子树。
⑹C
⑺D
⑻C完全二叉树中左子树的深度与右子树的深度相等或大1
C因为0是00的前缀,1是11的前缀。
B因为线索就是用来记录某结点的前驱或后继的。
(11)C因为有n+1个空链域来作为线索
(12)B
(13)D
(14)D
(15)D因为这些分支结点共有N1+2*N2+…+m*Nm个指针域,其中有N1+N2+…+Nm-1个指针指向分支结点,所以余下的指针指向的是叶子结点。
(16)A森林的第1棵树形成根及左子树
3.【证明】因为在满二叉树中没有度为1的结点,所以有:
n=n0+n2
设B为树中分枝数,则
n=B+1
所以
B=n0+n2-1
再由二叉树性质:
n0=n2+1
代入上式有:
B=n0+n0-1-1=2(n0-1)
4.
(1)以各字符出现的次数作为叶子结点的权值构造的哈夫曼编码树如下图所示。
其带权路径长度=2×5+1×5+3×4+5×3+9×2+4×3+4×3+7×2=98,所以,该字符串的编码长度至少为98位。
(2)1)已知深度为k的二叉树最多有2k-1个结点(K≥1),29-1<892<210-1,故树的高度为10;
2)对于完全二叉树来说,度为1的结点只能是0或1,因为n=n0+n1+n2和n0=n2+1
得:
设n1=0,892=n0+0+n2=2n2+1得n2不为整数出错
设n1=1,892=n0+1+n2=2n2+2得n2=445→n0=n2+1=446叶子结点数为446。
3)由2)得单支结点数为1
4)对于n个结点的完全二叉树,最后一个树叶结点,即序号为n的叶结点其双亲结点,即为最后一个非终端结点,序号为892/2=446。
(3)
1)先序序列和中序序列相同的二叉树为空树或任一结点均无左孩子的非空二叉树
2)中序和后序序列相同的二叉树为空树或任一结点均无右孩子的非空二叉树
3)先序和后序序列相同的二叉树为空树或仅有一个结点
(4)前序遍历:
ABDFJGKCEHILM
中序遍历:
BFJDGKACHELIM
后序遍历:
JFKGDBHLMIECA
层序遍历:
ABCDEFGHIJKLM
5.算法设计
⑴本算法不是要打印每个结点的值,而是求出结点的个数。
所以可将遍历算法中的“访问”操作改为“计数操作”,将结点的数目累加到一个全局变量中,每个结点累加一次即完成了结点个数的求解。
具体算法如下:
voidcount(BiNode*root)
{if(root){
count(root->lchild);
n++;
count(root->rchild);
}
}
⑵本算法的要求与前序遍历算法既有相同之处,又有不同之处。
相同之处是打印次序均为前序,不同之处是此处不是打印每个结点的值,而是打印出其中的叶子结点,即为有条件打印。
为此,将前序遍历算法中的访问操作