数据结构.docx
《数据结构.docx》由会员分享,可在线阅读,更多相关《数据结构.docx(12页珍藏版)》请在冰豆网上搜索。
数据结构
第1章绪论
第1章绪论
内容提要
1.数据由数据元素按照一定的逻辑结构组织而成,而每个数据元素通常又由一个或多个数据项顺序排列而成。
同一数据元素的各数据项之间无任何次序关系,则说它们之间为集合结构。
2.数据的逻辑结构可概括为四种类型:
集合结构、线性结构、树形结构和图状结构。
由它们的组合可以构成任何更为复杂的逻辑结构。
3.在数据的集合结构中,不考虑数据之间的任何次序,它们处于无序的、各自独立的状态;在线性结构中,数据之间是1对1的关系;在树形结构中,数据之间是1对多的关系;在图状结构中,数据之间是多对多的关系。
4.数据的存储结构可概括为四种类型:
顺序结构、链接结构、索引结构和散列结构,由它们的组合可以构成任何更为复杂的存储结构。
5.一个数组占有一块连续的存储空间,每个元素的物理存储单元是按下标位置从0开始连续编号的,相邻元素其存储位置也相邻。
对于任一种数据的逻辑结构,若能够把元素之间的逻辑关系对应地转换为数组下标位置之间的物理关系,则就能够利用数组来实现其顺序存储结构。
6.数据的逻辑结构需要采用图形来表示,数据的存储结构既需要采用图形来表示,又需要采用一种计算机语言的数据类型来定义。
7.数据的一种逻辑结构可以通过不同的存储结构来实现,反过来,一种存储结构可以用来实现不同的逻辑结构。
具体采用何种存储结构来存储数据(包括存储所有数据元素及相互之间的逻辑结构),由对数据的运算要求而定。
8.实现一个运算的方法和步骤称为此运算的算法。
描述一个算法有许多形式,如采用流程图、自然语言、计算机程序设计语言等。
在数据结构课程中主要使用某一种计算机程序设计语言来具体描述。
本书采用通用性强的C语言来描述。
9.一个算法的性能主要从四个方面来评价:
正确性、可读性、健壮性、有效性(即程序运行时的时间复杂度和空间复杂度)。
10.一个算法的时间或空间复杂度可分为最好、最差(坏)和平均这三种情况。
通常只关心最差或平均的情况,并且以讨论时间复杂度为主,因为节省时间比空间更重要。
11.算法的时间或空间复杂度的数量级采用大写的O表示,通常有常量级、对数级、线性级、线性与对数乘积级、平方级、立方级、指数级等级别,对应量级表示依次为O
(1),O(log2n),O(n),O(nlog2n),O(n2),O(n3),O(2n) 等。
当n较大时,量级越靠前的算法,其运行时间越短或占用空间越少,也就是说算法的时间或空间复杂度越好。
习题一
1.1有下列几种用二元组表示的数据结构,试画出它们分别对应的图形表示(当出现多个关系时,对每个关系画出相应的结构图),并指出它们分别属于何种结构。
1.A=(K,R)其中
K={a1,a2,a3,…,an}
R={}
2.B=(K,R),其中
K={a,b,c,d,e,f,g,h}
R={,,,,,,}
3.C=(K,R),其中
K={a,b,c,d,e,f,g,h}
R={,,,,,,}
4.D=(K,R),其中
K={1,2,3,4,5,6}
R={(1,2),(2,3),(2,4),(3,4),(3,5),(3,6),(4,5),(4,6)}
5.E=(K,R),其中
K={48,25,64,57,82,36,75,43}
R={r1,r2,r3}
r1={<48,25>,<25,64>,<64,57>,<57,82>,<82,36>,<36,75>,<75,43>}
r2={<48,25>,<48,64>,<64,57>,<64,82>,<25,36>,<82,75>,<36,43>}
r3={<25,36>,<36,43>,<43,48>,<48,57>,<57,64>,<64,75>,<75,82>}
1.2用C语言函数编写下列每一个算法,并分别求出它们的时间复杂度。
1.比较同一简单类型的两个数据x1和x2的大小,对于x1>x2,x1==x2和x1'、'=' 和'<' 字符。
假定简单类型用SimpleType表示,它可通过typedef语句定义为任一简单类型。
2.将一个字符串中的所有字符按相反的次序重新放置。
3.求一维double型数组a[n]中的所有元素之乘积。
4.计算
的值。
5.假定一维整型数组a[n]中的每个元素值均在[0,200]区间内,分别统计出落在[0,20],[20,50],[50,80],[80,130],[130,200]等各区间内的元素个数。
6.从二维整型数组a[m][n]中查找出最大元素所在的行、列下标。
1.3指出下列各算法的功能并求出其时间复杂度。
1.intPrime(intn)
{
inti=1;
intx=(int)sqrt(n);
while(++i<=x)
if(n%i==0)break;
if(i>x)return1;
elsereturn0;
}
2.intsum1(intn)
{
inti,p=1,s=0;
for(i=1;i<=n;i++){
p*=i;
s+=p;
}
returns;
}
3.intsum2(intn)
{
inti,j,s=0;
for(i=1;i<=n;i++){
intp=1;
for(j=1;j<=i;j++)
p*=j;
s+=p;
}
returns;
}
4.intfun(intn)
{
inti=1,s=1;
while(ss+=++i;
returni;
}
5.voidmtable(intn)
{
inti,j;
for(i=1;i<=n;i++){
for(j=i;j<=n;j++)
printf("%d*%d=%d\n",i,j,i*j);
printf("\n");
}
}
6.voidcmatrix(inta[M][N],intd)
/*M和N为全局整型常量*/
{
inti,j;
for(i=0;ifor(j=0;ja[i][j]*=d;
}
7.voidmatrimult(inta[M][N],intb[N][L],intc[M][L])
/*M,N和L均为全局整型常量*/
{
inti,j,k;
for(i=0;ifor(j=0;jc[i][j]=0;
for(i=0;ifor(j=0;jfor(k=0;kc[i][j]+=a[i][k]*b[k][j];
}
参考解答
1.1解答(略)。
1.2用C语言函数编写下列每一个算法,并分别求出它们的时间复杂度。
参考解答包含在下面程序中。
#include
#include
#defineM10
#defineN10
typedefintSimpleType;
charCompare(SimpleTypex1,SimpleTypex2)
{
if(x1>x2)return'>';
elseif(x1==x2)return'=';
elsereturn'<';
}
/*时间复杂度为O
(1)*/
voidReverse(char*p)
{
inti;
intn=strlen(p);
for(i=0;icharch;
ch=p[i];
p[i]=p[n-i-1];
p[n-i-1]=ch;
}
}
/*时间复杂度为O(n),n表示字符串的长度*/
doubleProduct(doublea[],intn)
{
inti;
doublep=1;
for(i=0;ip*=a[i];
returnp;
}
/*时间复杂度为O(n)*/
doubleAccumulate(doublex,intn)
{
doublep=1,s=1,i;
for(i=1;i<=n;i++){
p*=x;
s+=p/(i+1);
}
returns;
}
/*时间复杂度为O(n)*/
intCount(inta[],intn,intc[5])
/*用数组c[5]保存统计结果*/
{
intd[5]={20,50,80,130,201};/*用来保存各统计区间的上限*/
inti,j;
for(i=0;i<5;i++)c[i]=0;/*给数组c[5]中的每个元素赋初值0*/
for(i=0;i{
if(a[i]<0||a[i]>200)
return0;/*返回数值0表示数组中数据有错,统计失败*/
for(j=0;j<5;j++)/*查找a[i]所在的区间*/
if(a[i]c[j]++;/*使统计相应区间的元素增1*/
}
return1;/*返回数值1表示统计成功*/
}
/*时间复杂度为O(n)*/
voidfind(inta[M][N],intm,intn,int*Lin,int*Col)
/*M和N为全局常量,应满足M>=n和N>=n的条件,Lin和Col为指针形参,用其所指向的实参带回行、列值*/
{
inti,j;
*Lin=0;*Col=0;
for(i=0;ifor(j=0;jif(a[i][j]>a[*Lin][*Col]){*Lin=i;*Col=j;}
}
/*时间复杂度为O(m*n)*/
voidmain()
{
intx=20,y=30;
charp[12]="123456789";
doublea[5]={1,2,3,4,5};
intb[8]={23,10,56,125,38,44,130,98};
intc[3][N]={{1,3,5,7},{10,8,6,4},{4,12,8,3}};
intd[5];
inti,c1,c2;
printf("%c\n",Compare(x,y));
Reverse(p);
printf("%s\n",p);
printf("%f\n",Product(a,5));
printf("%f\n",Accumulate(1.5,3));
Count(b,8,d);
for(i=0;i<5;i++)printf("%4d",d[i]);printf("\n");
find(c,3,4,&c1,&c2);
printf("%4d%4d%4d\n",c1,c2,c[c1][c2]);
}
1.3指出下列各算法的功能并求出其时间复杂度。
参考解答如下。
1.判断n是否是一个素数,若是则返回数值1,否则返回0。
该算法的时间复杂度为
。
2.计算
的值。
时间复杂度为O(n)。
3.计算
的值。
时间复杂度为O(n2)。
4.求出满足不等式1+2+3++i≥n的最小i值。
时间复杂度为
。
提示:
因为1+2+3+…+i=(1+i)i/2,即当n很大时i的平方与n成正比,所以i的值(即函数中while循环的次数)与n的平方根成正比。
5.打印出一个具有n行的乘法表,第i行 (1≤i≤n) 中有n–i+1个乘法项,每个乘法项为i与j(i≤j≤n) 的乘积。
时间复杂度为O(n2)。
6.使数组a[M][N]中的每一个元素均乘以d的值。
时间复杂度为O(M×N)。
7.矩阵相乘,即a[M][N]×b[N][L]→c[M][L]。
时间复杂度为O(M×N×L)。
第2章线性表
第2章线性表
内容提要
1.线性表是线性数据结构,通常采用顺序或链接方式进行存储。
2.线性表的顺序存储类型定义为structList,它包含三个域:
第一个为list域,用该域指向的动态分配的数组空间用于存储线性表中的所有元素;第二个为size域,用于存储线性表的当前长度,即线性表中当前所含元素的个数;第三个为MaxSize域,用来存储list所指动态数组的大小。
3.顺序存储线性表的结构类型structList可定义如下:
structList{
ElemType*list;/*存线性表元素的动态存储空间的指针*/
intsize;/*存线性表的长度*/
intMaxSize;/*存list数组的长度,亦即所能存储线性表的最大长度*/
};
4.线性表的链接存储是通过定义结点类型和表头指针实现的,各结点按照所存元素之间的线性关系依次链接起来并通过表头指针标识,这样就构成了线性表的链接存储结构。
5.单链表中的结点类型可定义为structsNode,它包含有data和next两个域,用于分别存储一个元素和一个指向后继结点的指针,具体描述为:
structsNode/*定义单链表结点类型*/
{
ElemTypedata;
structsNode*next;
};
6.在线性表的顺序存储和链接存储结构上,都能够实现对线性表的任何运算,如插入、删除、查找、更新一个元素的运算。
7.在线性表的顺序存储结构的某个位置上插入或删除元素需要移动后面的所有元素,而在线性表的链接存储结构的某个位置上插入和删除结点则不需要移动任何元素(结点)。
在线性表的顺序存储结构上可以按元素编号直接存取到相应元素,而在线性表的链接存储结构上必须从表头指针开始依次访问前面每个结点之后,才能够访问到相应编号的元素(即结点值)。
8.当用顺序存储表示一个集合时,由于可以按任一种次序存储元素,所以为了节省时间,应该把新元素插入到表尾位置,当从任一位置删除一个元素后,应该把表尾元素填补到被删除而空出的位置上,这样得到的时间复杂度均为O
(1)。
9.当用链接存储表示一个集合时,由于可以按任一种次序存储元素,所以为了节省时间,应该把新元素插入到表头位置,这样得到的时间复杂度为O
(1)。
10.若让单链表的表尾结点的指针域指向表头结点,就构成了循环单链表;若让双向链表的表头结点的左指针left域指向表尾结点,让表尾结点的右指针域指向表头结点,就构成了循环双向链表。
在链表前都可以加上一个附加表头结点,从而构成带头结点的链表或循环链表。
这些特殊形式的链表能够给一些运算带来方便。
习题二
2.1假定线性表La的类型为structList,元素类型ElemType为int,请写出下列每个主函数的运行结果。
1.voidmain()
{
inti;
structListLa;
inta[]={48,26,57,34,62,79};
InitList(&La,5);
for(i=0;i<6;i++)
InsertFirstList(&La,a[i]);
TraverseList(&La);
}
2.voidmain()
{
inti;
structListLa;
inta[]={48,26,57,34,62,79};
InitList(&La,5);
for(i=0;i<6;i++)
InsertOrderList(&La,a[i]);
TraverseList(&La);
}
3.voidmain()
{
inti;
structListLa;
inta[]={48,26,57,34,62,79};
InitList(&La,5);
for(i=0;i<6;i++)
InsertLastList(&La,a[i]);
InsertPosList(&La,3,65);
DeleteFirstList(&La);
DeleteLastList(&La);
TraverseList(&La);
}
4.voidmain()
{
inti;
structListLa;
inta[]={48,26,57,34,62,79};
InitList(&La,5);
for(i=0;i<6;i++)
InsertPosList(&La,i+1,a[i]);
InsertPosList(&La,3,DeleteFirstList(&La));
InsertFirstList(&La,DeleteLastList(&La));
DeleteValueList(&La,62);
TraverseList(&La);
}
2.2改写2.1题中的每个主函数,使其采用单链表实现,并保持算法功能和运行结果不变。
2.3对于structList类型的线性表,编写出下列每个算法。
1.从线性表中删除具有最小值的元素并由函数返回,空出的位置由最后一个元素填补,若线性表为空则显示出错信息并退出运行。
2.从线性表中删除值在给定值s和t之间(要求s小于等于t)的所有元素。
3.从线性表中删除所有值重复的元素,使所有元素的值均不同。
如对于线性表(2,8,9,2,5,5,6,8,7,2),则执行此算法后变为(2,8,9,5,6,7)。
4.根据一维数组中的n个元素建立一个有序线性表。
5.从有序线性表中删除值在给定值s和t(要求s小于等于t)之间的所有元素。
2.4对于结点类型为structsNode的单链表,编写出下列每个算法。
1.将一个单链表按逆序链接,即若原单链表中存储元素的次序为a1,a2,…,an,则逆序链接后变为an,an–1,…,a1。
假定仍使用原有结点。
2.从单链表中查找出所有元素的最大值,该值由函数返回,若单链表为空,则显示出错信息并停止运行。
3.统计出单链表中结点的值等于给定值x的结点数。
4.根据一维数组a[n]建立一个单链表,使单链表中元素的次序与a[n]中元素的次序相同,并使该算法的时间复杂度为O(n)。
5.将一个单链表重新链接成有序单链表。
*2.5按照下列每个题目的要求编写算法。
1.编写一个算法,使用带表头附加结点的循环单链表解决约瑟夫(Josephus)问题。