L.data[k-1]=L.data[k];/*删除位置后继元素逐一前移*/
}
L.length--;
returnOK;
}
时间复杂度:
最好O
(1)最坏O(n)平均O(n)(依赖于插入位置i、表长n)
④查找(按位获取操作)
#defineOK1
#defineERROR0
/*初始条件:
线性表L已经存在,i∈[1,ListLength(L)]*/
/*操作结果:
将L中第i个位置的元素返回给e*/
intGetElem{SqListL,inti,ElemType*e}
{
if(L.length==0||i<1||i>L.length)
returnERROR;
*e=L.data[i-1];
returnOK;
}
时间复杂度:
O
(1)
⑵单链表
①建表
Node*ListCreatTail()/*尾插法建立带头结点的单链表,链表长度为n,链表元素从屏幕读取,返回头结点位置*/
{
LinkListp,r,L;
intn,i;
ElemTyped;
L=(LinkList)newNode;
printf("\n请输入链表L的数据个数:
\n");
scanf("%d",&n);
L->data=n;
L->next=Null;
r=L;
for(i=1;i<=n;i++)
{
p=(Node*)newNode;
printf("结点%d=",i);
scanf("%d",&d);
p->data=d;
p->next=Null;
r->next=p;
r=p;
printf("\nNode%d=%d\n",i,p->data);
}
printf("\n");
returnL;
}
时间复杂度:
O(n)
②插入
链表的插入:
一定,二建,三后,四前
StatusListInsert{LinkListL,inti,ElemTypee}
{
Node*p,*q;
p=GetElem(L,i-1);
if(p==Null)/*链表为空*/
returnERROR;
q=(Node*)malloc(sizeof(Node));
q->data=e;
q–>next=p–>next;
p–>next=q;
returnOK;
}
时间复杂度:
O
(1)
③删除
链表的删除:
一定,二连,三放
StatusListDelete{LinkListL,inti,ElemType*e}
{
Node*p,*q;
p=GetElem(L,i-1);
if(p==Null||p->next==Null)/*链表为空*/
returnERROR;
q=p->next;
*e=q->data;
p–>next=q->next;
free(q);
returnOK;
}
时间复杂度:
O
(1)
④查找
a.按位获取
//链表不是随机存取结构
Node*GetElem{LinkListL,inti}
{
intk;/*k为计数器*/
LinkListp;/*指针p指向当前结点*/
p=L;/*指针p指向头结点*/
k=1;
while(p&&k
{
p=p->next;/*p指向下一个结点*/
++k;/*计数器加1*/
}
if(k==i)/*结点i存在*/
returnp;
else
returnNull;
}
时间复杂度:
最好O
(1)最坏O(n)
b.按值获取
/*初始条件:
线性表L已经存在*/
/*操作结果:
定位L中与给定值相等的元素,返回其位置*/
Node*LocateElem{LinkListL,ElemTypee}
{
LinkListp;/*指针p指向当前结点*/
p=L;/*指针p指向头结点*/
while(p&&p->data!
=e)
p=p->next;/*p指向下一个结点*/
if(!
p)/*结点i不存在*/
returnNull;
else
returnp;
}
时间复杂度:
最好O
(1)最坏O(n)
3.了解双向链表和循环链表的定义和特点及插入、删除操作的实现(见书和PPT)
二、栈和队列
考点:
1.栈和队列的逻辑结构和两种存储表示方法
2.定义在逻辑结构上的各种基本运算
InitStack(&S)
操作结果:
初始化一个空的栈S。
DestroyStack(&S)
初始条件:
栈S已经存在。
操作结果:
销毁栈S。
ClearStack(&S)
初始条件:
栈S已经存在。
操作结果:
将栈S清空。
StackEmpty(S)
初始条件:
栈S已经存在。
操作结果:
若栈S为空返回true,否则返回false。
StackLength(S)
初始条件:
栈S已经存在。
操作结果:
返回栈S的元素个数。
GetTop(S,&e)
初始条件:
栈S已经存在且非空。
操作结果:
将S的栈顶元素返回给e。
Push(&S,e)
初始条件:
栈S已经存在且非空。
操作结果:
新元素e入栈。
Pop(&S,&e)
初始条件:
栈S已经存在且非空。
操作结果:
删除S的栈顶元素,并用e返回其值。
3.在各种存储结构上如何实现这些基本运算
4.各种基本运算的时间复杂性
5.栈的常用应用
要求:
1.理解栈和队列的异同点、栈和队列与一般线性表的不同
2.了解顺序栈的溢出和顺序队列的假溢问题
⑴顺序栈的溢出
①上溢(overflow):
栈满时再压入数据
②下溢(underflow):
栈空时再弹出数据
栈满:
S.top==MAXSIZE–1
空栈:
S.top==–1
3.熟练掌握顺序栈(两栈共享空间)、链栈、循环队列、链队列上的队列初始化、元素插入、删除的算法实现及相关的复杂度分析
⑴顺序栈(两栈共享空间)初始化
StatusInitDuStack(Sqstack&S)
{
S.top1==-1;
S.top2==MAXSIZE;
returnOK;
}
时间复杂度O
(1)
⑵顺序栈(两栈共享空间)元素插入
/*新元素e入栈*/
StatusPush{SqDuStackS,SElemTypee,intStackNum}
{
if(S.top1+1==S.top2)/*栈满*/
returnERROR;
if(StackNum==1)/*新元素压入栈1*/
S.top1++;
S.data[S.top1]==e;/*新元素入栈*/
returnOK;
elseif(StackNum==2)
S.top2--;
S.data[S.top2]==e;/*新元素入栈*/
returnOK;
elsereturnERROR;
}
时间复杂度O
(1)
⑶顺序栈(两栈共享空间)元素删除[来自网络]
//若栈不空,则删除s的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
StatusPop(SqDuStack*S,ElemType*e,intstackNumber)
{
if(stackNumber==1)
{
if(S->top1==1)
returnERROR;//说明栈1已经是空栈,溢出
*e=S->data[s->top1--];//将栈1的栈顶元素出栈
}
elseif(stackNumber==2)
{
if(S->top2==MAXSIZE)
returnERROR;//说明栈2已经是空栈,溢出
*e=S->data[S->top2++];//将栈2的栈顶元素出栈
}
returnOK;
}
时间复杂度O
(1)
⑷链栈初始化[来自网络]
voidInitLinkStack(linkStack&S)
{
S=newSNode;
S->next=NULL;
}
时间复杂度O
(1)
⑸链栈元素插入
/*新元素e入栈*/
StatusPush(LinkStack*S,SElemTypee)
{
SNode*p=(SNode*)malloc(sizeof(SNode));/*动态申请结点*/
p->data=e;/*结点赋值*/
p->next=S->top;/*新结点插到表头*/
S->top=p;/*栈顶指针改为新结点*/
S->length++;
returnOK;
}
时间复杂度O
(1)
⑹链栈元素删除
/*栈顶元素出栈*/
StatusPop(LinkStack*S,SElemType*e)
{
StackNode*p;
if(StackEmpty(*S))
returnERROR;
p=S->top;/*p指向出栈结点*/
*e=p->data;/*返回栈顶元素*/
S->top=p->next;/*栈顶指针下移*/
free(p);/*释放结点空间*/
S->length--;
returnOK;
}
(7)循环队列的初始化
/*队列初始化*/
StatusIntiQueue(SqQueue*Q)
{
Q->front=0;
Q->rear=0;
returnOK;
}
/*求队列长度*/
intQueueLength(SqQueueQ)
{
return(Q.rear–Q.front+MAXSIZE)%MAXSIZE;
}
时间复杂度O
(1)
(8)循环队列元素插入
/*新元素e入队列*/
StatusEnQueue(SqQueue*Q,QElemTypee)
{
if((Q->rear+1)%MAXSIZE==Q->front)
returnERROR;/*队满*/
Q->data[Q->rear]=e;/*新元素入队*/
Q->rear=(Q->rear+1)%MAXSIZE;/*rear指针后移,若到最后则转到数组头部*/
returnOK;
}
时间复杂度O
(1)
(9)循环队列元素删除[由书P65修改]
//队头元素出队
Status DeQueue(SqQueue*Q,QElemTypee)
{
if(front==rear)returnERROR;/*队空*/
e=Q->data[Q->front];
Q->front=(Q->front+1)%MAXSIZE;/*front指针后移,若到最后则转到数组头部*/
returnOK;
}
(10)链队列的初始化
StatusInitQueue(LinkQueue&Q)
{
Q.front=Q.rear=(QNode*)malloc(sizeof(QNode));
if(!
Q.front)exit(OVERFLOW);
Q.front->next=NULL;
returnOK;
}
时间复杂度O
(1)
(11)链队列的元素插入
//*新元素e入队列*/
StatusEnQueue(LinkQueue*Q,QElemTypee)
{
QNode*p=(QNode*)malloc(sizeof(QNode));/*动态申请结点,C语言*/
//QNode*p=NewQNode;/*动态申请结点,C++*/
if(!
p)/*存储分配失败*/
exit(OVERFLOW);
p->data=e;/*结点赋值*/
p->next=Null;
Q->rear->next=p;/*新结点插到队尾*/
Q->rear=p;/*新结点设置为队尾*/
returnOK;
}
时间复杂度O
(1)
(12)链队列的元素删除
/*队头元素出队列*/
StatusDeQueue(LinkQueue*Q,QElemType*e)
{
QNode*p;
if(Q->front==Q->rear)/*队空*/
returnERROR;
p=Q->front->next;/*p指向要出队元素所在结点*/
*e=p->data;/*用e返回出队元素*/
Q->front->next=p->next;/*头结点指向出队元素的下一个结点*/
if(Q->rear==p)/*出队前只有一个元素*/
Q->rear=Q->front;
free(p);
returnOK;
}
时间复杂度O
(1)
4.了解栈的常用应用,理解利用栈实现递归的原理,掌握逆波兰式的生成和计算
(1)栈的常用应用:
数值转换、括号匹配、算术表达式求解、迷宫求解、实现递归
(2)利用栈实现递归的原理:
见栈和队列3课件
(3)逆波兰式生成和计算
逆波兰表示:
所有的符号都在运算数字后面出现
例:
9+(3–1)*3+10/2中缀表示法
931–3*+102/+后缀表示法,不再需要括号
计算:
遇到数字就进栈
见到符号就运算
运算结果也进栈
最终结果栈底见
生成:
遇到数字就输出
符号进栈要比较
低级平级都得等
高于栈顶才能进
左边括号等匹配
右括号来了一起闪
三、串
考点:
1.串的逻辑结构和两种存储表示方法
2.堆存储串的基本运算及复杂度分析
3.串的模式匹配
要求:
1.理解串与线性表的不同(数据对象、操作对象)
串:
数据对象:
字符集操作对象:
子串
线性表:
数据对象:
集合操作对象:
线形表
2.掌握串的复制操作的算法及复杂度分析
3.理解串的模式匹配思想及两种基本的模式匹配算法,掌握next数组的计算方法
(1)BruteForce算法——朴素匹配算法
思路:
从主串S第i位开始的m位字符串,依次与T第1位-第m位比较,若相等,返回比较之前的i值;否则i退回,从下一位开始比较;如果从S的所有位开始都不能成功匹配T,返回0.
intIndex(SStringS,SStringT)
{
inti=1,j=1;
while(i<=S[0]&&j<=T[0])
{
if(S[i]==T[j]){i++;j++;}//比较T中下一字符
else{i=i–j+2;j=1;}//i回溯,j回溯,重新开始匹配
}
if(j>T[0])returni-T[0];
elsereturn0;
}
时间复杂度O(m*n)最坏:
m(n-m+1)
(2)KMP算法(克努特—莫里斯—普拉特算法)
思路:
失配时,i不回溯,T尽量多滑动一段距离
intIndex_KMP(SStringS,SStringT)
{
inti=1,j=1;
while(i<=S[