自学考试《数据结构》各章要点Word格式.docx
《自学考试《数据结构》各章要点Word格式.docx》由会员分享,可在线阅读,更多相关《自学考试《数据结构》各章要点Word格式.docx(24页珍藏版)》请在冰豆网上搜索。
数据的存储结构可用以下四种基本的存储方法得到;
顺序存储法:
该方法是把逻辑上相邻的结点存储在物理位置上相邻的存储单元里,结点间的逻辑关系由存储单元的邻接关系来体现。
由此得到的存储表示称为顺序存储结构。
链接存储方法:
该方法不要求逻辑上相邻的结点在物理位置上亦相邻,结点间的逻辑关系是由附加的指针字段表示的。
由此得到的存储表示称为链式存储结构。
索引存储方法:
若每个结点在索引表中都有一个索引项,则该索引表称之为稠密索引。
若一组结点在索引表中只对应一个索引项,则该索引表称为稀疏索引。
散列存储方法:
该方法的基本思想是根据结点的关键字直接计算出该结点的存储地址。
算法是一个良定义的计算过程,以一个或多个值输入,并以一个或多个值输出。
评价算法的好坏的因素:
算法是正确的;
执行算法的时间;
执行算法的存储空间(主要是辅助存储空间);
算法易于理解、编码、调试。
时间复杂度:
是某个算法的时间耗费,它是该算法所求解问题规模n的函数。
渐近时间复杂度:
是指当问题规模趋向无穷大时,该算法时间复杂度的数量级。
评价一个算法的时间性能时,主要标准就是算法的渐近时间复杂度。
算法中语句的频度不仅与问题规模有关,还与输入实例中各元素的取值相关。
时间复杂度按数量级递增排列依次为:
常数阶O
(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n^2)、立方阶O(n^3)、……k次方阶O(n^k)、指数阶O(2^n)。
空间复杂度:
是某个算法的空间耗费,它是该算法所求解问题规模n的函数。
算法的时间复杂度和空间复杂度合称算法复杂度。
第二章线性表
线性表是由n≥0个数据元素组成的有限序列。
n=0是空表;
非空表,只能有一个开始结点,有且只能有一个终端结点。
线性表上定义的基本运算:
构造空表:
Initlist(L)
求表长:
Listlength(L)
取结点:
GetNode(L,i)
查找:
LocateNode(L,x)
插入:
InsertList(L,x,i)
删除:
Delete(L,i)
顺序表是按线性表的逻辑结构次序依次存放在一组地址连续的存储单元中。
在存储单元中的各元素的物理位置和逻辑结构中各结点相邻关系是一致的。
地址计算:
LOCa(i)=LOCa
(1)+(i-1)*d;
(首地址为1)/考试大收集整理/
顺序表是一种随机存取结构。
在顺序表中实现的基本运算:
VoidInsertList(SeqList﹡L,DataTypex,inti)
{//将新结点x插入L所指的顺序表的第i个结点ai的位置上
intj;
if(i<
1‖i>
L->
length+1)
Error(〝positionerror〞);
//非法位置,退出运行
if(L->
length>
=ListSize)
Error(〝overflow〞);
//表空间溢出,退出运行
for(j=L->
length-1;
j>
=i-1;
j--)
L->
data[j+1]=L->
data[j];
//结点后移
data[i-1]=x;
//插入x
length++;
//表长加1
}
平均移动结点次数为n/2;
平均时间复杂度均为O(n)。
平均移动结点次数为(n-1)/2;
线性表的链式存储结构中结点的逻辑次序和物理次序不一定相同,为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还存储了其后继结点的地址信息(即指针或链)。
这两部分信息组成链表中的结点结构。
一个单链表由头指针的名字来命名。
单链表运算:
建立单链表
用C语言描述的单链表如下:
typedefcharDataType;
//假设结点的数据域类型是字符
typedefstructnode{//结点类型定义
DataTypedata;
//结点的数据域
structnode﹡next;
//结点的指针域
}ListNode;
typedefListNode﹡LinkList;
ListNode﹡P;
LinkListhead;
头插法:
s->
next=head;
head=s;
生成的顺序与输入顺序相反。
LinkListCreateListF(void)
{//返回单链表的头指针
charch;
LinKListhead;
//头指针
ListNode﹡S;
//工作指针
head=NULL;
//链表开始为空
ch=getchar();
//读第1个字符
while(ch!
=‘﹨n’){
s=(ListNode﹡)malloc(sizeof(ListNode));
//生成新结点,对应图2.7的①
s->
data=ch;
//将读入的数据放入新结点的数据域中,对应图2.7的②
s–>
next=head;
//对应图2.7的③
head=s;
//对应图2.7的④
//读入下一字符
Returnhead;
//返回头指针
head
s
图2.7将结点﹡s插到单链表head的头上
尾插法:
head=rear=null;
if(head=null)head=s;
elser->
next=s;
r=s;
平均时间复杂度均为O(n)
尾插法建表算法如下:
{
Charch;
ListNode﹡S,﹡r;
r=NULL;
//链表初值为空,尾指针初值为空
while((ch=getchar())!
s=(ListNode﹡)malloc(sizeof(ListNode));
//对应图2.8①
//对应图2.8②
if(head=NULL)
head=s;
//新结点插入空表
else
r–>
next=s;
//新结点插入非空表的尾结点﹡r之后,对应图2.8③
r=s;
//尾指针指向新表尾,对应图2.8④
}//endofwhile
if(r!
=NULL)
r->
next=NULL;
//对于非空表,将尾结点指针域置空
returnhead;
head
图2.8将新结点﹡s插到单链表head的尾上
尾插法建立单链表的算法可简化为:
{//用尾插法建立带头结点的单链表
charch;
LinListhead=(LinList)malloc(sizeof(ListNode));
//生成头结点
ListNode﹡s,﹡r;
r=head;
//尾指针初值亦指向头结点
while((ch=getchar())!
=ˊ﹨nˊ){
s=(ListNode﹡)malloc(sizeof(ListNode));
s->
r->
r=s;
r->
next=NULL;
//终端结点的指针域置空,或空表的头结点指针域置空
}
加头结点的算法:
对开始结点的操作无需特殊处理,统一了空表和非空表。
查找
按序号:
与查找位置有关,平均时间复杂度均为O(n)。
ListNode﹡GetNode(LinkListhead,inti)
{//在带头结点的单链表head中查找第i个结点,若找到(0≤i≤n),则返回该结点的存储
//位置,否则返回NULL。
ListNode﹡p;
P=head;
j=0;
//从头结点开始扫描
while(p->
next﹠﹠j<
i){//顺指针向后扫描,直到p->
next为NULL或j=i为止
p=p->
next;
j++;
if(i==j)
returnp;
//找到了第i个结点
returnNULL;
//当i<
0或i>
n时,找不到第i个结点
按值:
与输入实例有关,平均时间复杂度均为O(n)。
ListNode﹡LocateNode(LinkListhead,DataTypekey)
{//在带头结点二队单链表head中查找其值为key的结点
ListNode﹡p=head->
//从开始结点比较,表非空时,p初始值指向开始结点。
while(p﹠﹠p->
data!
=key)//直到p为NULL或p->
data为key止
p=p->
next;
//扫描下一结点
//若p=NULL则查找失败,否则p指向找到的值为key的结点
插入运算:
p=GetNode(L,i-1);
next=p->
next;
p->
平均时间复杂度均为O(n)
voidInsertList(LinkListhead,DataTypex,inti)
{//将值为x的新结点插入到带头结点的单链表head的第i个结点的位置上
ListNode﹡p
P=GetNode(head,i-1);
//寻找第i-1个结点,对应图2.10①
if(p==NULL)//i<
1或i>
n+1时插入位置i有错
Error(〞positionerror〞);
s=(ListNode﹡)malloc(sizeof(ListNode));
//对应图2.10②
s->
data=x;
//对应图2.10③
//对应图2.10④
p->
next=s;
//对应图2.10⑤
图2.10在单链表上插入结点示意图
删除运算:
r=p->
next=r->
free(r);
voidDeleteList(LinkListhead,inti)
{//删去带头结点的单链表head上的第i个结点
ListNode﹡p,﹡r;
//找第i-1个结点,
if(p==NULL‖p->
next==NULL)//i<
n时删除位置有错
//退出程序运行
r=p->
//令r指向被删结点ai
next=r->
//将ai从链上摘下
free(r);
//释放结点ai,将所占用的空间归还给存储池
单循环链表是一种首尾相接的单链表,终端结点的指针域指向开始结点或头结点。
链表终止条件是以指针等于头指针或尾指针。
采用单循环链表在实用中多采用尾指针表示单循环链表。
优点是查找头指针和尾指针的时间都是O
(1),不用遍历整个链表。
双链表就是双向链表,就是在单链表的每个结点里再增加一个指向其直接前趋的指针域prior,形成两条不同方向的链。
由头指针head惟一确定。
双链表的前插操作算法如下:
voidDInsertBefore(DListNode﹡p,DataTypex)
{//在带头结点的双链表中,将值为x的新结点插入﹡p之前,设p≠NULL
DListNode﹡s=malloc(sizeof(DListNode));
//
data=x;
//
prior=p->
prior;
next=p;
prior->
prior=s;
双链表也可以头尾相链接构成双(向)循环链表。
双链表上的插入和删除时间复杂度均为O
(1)。
双链表上删除结点﹡p自身的算法如下:
VoidDDeleteNode(DListNode﹡p)
{//在带头结点的双链表中,删去结点﹡p,设﹡p为非终端结点//
next=p->
next->
prior=p->
prior;
顺序表和链表的比较:
基于空间:
顺序表的存储空间是静态分配,存储密度为1;
适于线性表事先确定其大小时采用。
链表的存储空间是动态分配,存储密度<
1;
适于线性表长度变化大时采用。
基于时间:
顺序表是随机存储结构,当线性表的操作主要是查找时,宜采用。
以插入和删除操作为主的线性表宜采用链表做存储结构。
若插入和删除主要发生在表的首尾两端,则宜采用尾指针表示的单循环链表。
第三章栈和队列
栈(Stack)是仅限制在表的一端进行插入和删除运算的线性表,称插入、删除这一端为栈顶,另一端称为栈底。
表中无元素时为空栈。
栈的修改是按后进先出的原则进行的,我们又称栈为LIFO表(LastInFirstOut)。
通常栈有顺序栈和链栈两种存储结构。
整型量top来指示当前栈顶位置,通常称top为栈顶指针。
顺序栈的类型定义只需将顺序表的类型定义中的长度属性改为top即可。
栈的基本运算有六种:
构造空栈:
InitStack(S)
判栈空:
StackEmpty(S)
判栈满:
StackFull(S)
进栈:
Push(S,x)
退栈:
Pop(S)
取栈顶元素:
StackTop(S) 在顺序栈中有“上溢”和“下溢”的现象。
“上溢”是栈顶指针指出栈的外面是出错状态。
“下溢”可以表示栈为空栈,因此用来作为控制转移的条件。
顺序栈中的基本操作有六种:
构造空栈
判栈空
IntStackEmpty(SeqStack﹡s)
ReturnS->
top==-1;
判栈满
IntStackFull(SeqStack﹡s)
top==StackSize-1;
进栈
voidPush(SeqStack﹡s,DataTypex)
if(StackFull(S))
Error(“Stackoverflow”);
//上溢,退出运行
S->
data[++S->
top]=x;
//栈顶指针加1后将x进栈
退栈
DataTypePop(Seqstack﹡s)
if(StackEmpty(S))
Error(“Stackunderflow”);
//下溢,退出运行
data[S->
top--];
//栈顶元素返回后将栈顶指针减1
取栈顶元素
DataTypeStackTop((Seqstack﹡s)
Error(“Stackisempty”);
ReturnS->
data[S->
top];
链栈则没有上溢的限制,因此进栈不要判栈满。
链栈不需要在头部附加头结点,只要有链表的头指针就可以了。
链栈中的基本操作有五种:
判栈空
进栈
队列(Queue)是一种运算受限的线性表,插入在表的一端进行,而删除在表的另一端进行,允许删除的一端称为队头(front),允许插入的一端称为队尾(rear),队列的操作原则是先进先出的,又称作FIFO表(FirstInFirstOut).队列也有顺序存储和链式存储两种存储结构。
队列的基本运算有六种:
置空队:
InitQueue(Q)
判队空:
QueueEmpty(Q)
判队满:
QueueFull(Q)
入队:
EnQueue(Q,x)
Q->
rear=(Q->
rear+1)%Queuesize
出队:
DeQueue(Q)
front=(Q->
front+1)%Queuesize
取队头元素:
QueueFront(Q)
顺序队列的“假上溢”现象:
由于头尾指针不断前移,超出向量空间。
这时整个向量空间及队列是空的却产生了“上溢”现象。
为了克服“假上溢”现象引入循环向量的概念,是把向量空间形成一个头尾相接的环形,这时队列称循环队列。
判定循环队列是空还是满,方法有三种:
一种是另设一个布尔变量来判断;
第二种是少用一个元素空间,入队时先测试((rear+1)%m=front)?
满:
空;
第三种就是用一个计数器记录队列中的元素的总数。
队列的链式存储结构称为链队列,一个链队列就是一个操作受限的单链表。
为了便于在表尾进行插入(入队)的操作,在表尾增加一个尾指针,一个链队列就由一个头指针和一个尾指针唯一地确定。
链队列不存在队满和上溢的问题。
在链队列的出队算法中,要注意当原队中只有一个结点时,出队后要同进修改头尾指针并使队列变空。
第四章串
串是零个或多个字符组成的有限序列。
空串:
是指长度为零的串,也就是串中不包含任何字符(结点)。
空白串:
指串中包含一个或多个空格字符的串。
在一个串中任意个连续字符组成的子序列称为该串的子串,包含子串的串就称为主串。
子串在主串中的序号就是指子串在主串中首次出现的位置。
空串是任意串的子串,任意串是自身的子串。
串分为两种:
串常量在程序中只能引用不能改变;
串变量的值可以改变。
串的基本运算有:
求串长strlen(char*s)
串复制strcpy(char*to,char*from)
串联接strcat(char*to,char*from)
串比较charcmp(char*s1,char*s2)
字符定位strchr(char*s,charc)
。
串是特殊的线性表(结点是字符),所以串的存储结构与线性表的存储结构类似。
串的顺序存储结构简称为顺序串。
顺序串又可按存储分配的不同分为:
静态存储分配:
直接用定长的字符数组来定义。
优点是涉及串长的操作速度快,但不适合插入、链接操作。
动态存储分配:
是在定义串时不分配存储空间,需要使用时按所需串的长度分配存储单元。
串的链式存储就是用单链表的方式存储串值,串的这种链式存储结构简称为链串。
链串与单链表的差异只是它的结点数据域为单个字符。
为了解决“存储密度”低的状况,可以让一个结点存储多个字符,即结点的大小。
顺序串上子串定位的运算:
又称串的“模式匹配”或“串匹配”,是在主串中查找出子串出现的位置。
在串匹配中,将主串称为目标(串),子串称为模式(串)。
这是比较容易理解的,串匹配问题就是找出给定模式串P在给定目标串T中首次出现的有效位移或者是全部有效位移。
最坏的情况下时间复杂度是O((n-m+1)m),假如m与n同阶的话则它是O(n^2)。
链串上的子串定位运算位移是结点地址而不是整数。
第五章多维数组和广义表
除边界外,每个元素a(ij)都恰好有两个直接前趋和两个直接后继结点
数组一般用顺序存储的方式表示。
存储的方式有:
行优先顺序,也就是把数组逐行依次排列。
PASCAL、C
列优先顺序,就是把数组逐列依次排列。
FORTRAN
地址的计算方法:
按行优先顺序排列的数组:
LOCa(ij)=LOCa(11)+((i-1)*n+(j-1))*d.
按列优先顺序排列的数组:
LOCa(ij)=LOCa(11)+((j-1)*n+(i-1))*d. 矩阵的压缩存储:
为多个相同的非零元素分配一个存储空间;
对零元素不分配空间。
特殊矩阵的概念:
所谓特殊矩阵是指非零元素或零元素分布有一定规律的矩阵。
稀疏矩阵的概念:
一个矩阵中若其非零元素的个数远远小于零元素的个数,则该矩阵称为稀疏矩阵。
特殊矩阵的类型:
对称矩阵:
满足a(ij)=a(ji)。
元素总数n(n+1)/2.I=max(i,j),J=min(i,j),LOCa(ij)=LOC(sa[0])+(I*(I+1)/2+J)*d.
三角矩阵:
上三角阵:
k=i*(2n-i+1)/2+j-i,LOCa(ij)=LOC(sa[0])+k*d.
下三角阵:
k=i*(i+1)/2+j,LOCa(ij)=LOC(sa[0])+k*d.
对角矩阵:
k=2i+j,LOCa(ij)=