if(L.data[i]==e)returni+1;//下标为i的元素值等于e,返回其位号i+1
}
return0;
}//LocateElem
顺序表的归并:
T(n)=O(La.length+Lb.length)
voidMergeList_Sq(SqListLa,SqListLb,SqList&Lc){
//已知顺序表La和Lb的元素按值非递减排列
//归并La和Lb得到新的顺序表Lc,Lc的元素也按值非递减排列
pa=La.data;
pb=Lb.data;
Lc.ListSize=Lc.length=La.length+Lb.length;
pc=Lc.data=(ElemType*)malloc(Lc.ListSize*sizeof(ElemType));
if(!
Lc.data)exit(OVERFLOW);
pa_last=La.data+La.length-1;
pb_last=Lb.data+Lb.length-1;
while(pa<=pa_last&&pb<=pb_last){//开始归并
if(*pa<=*pb)*pc++=*pa++;
else*pc++=*pb++;
}
while(pa<=pa_last)*pc++=*pa++;
while(pb<=pb_last)*pc++=*pb++;
}//MergeList_Sq
线性表的链式存储(单链表):
单链表结构的定义:
typedefstructLNode{
Elemtypedata;
structLNode*next;
}LNode,*LinkList;
引入头结点的单链表:
头结点的数据域可以不设任何信息,也可以记录表长等信息,头结点的指针域指向线性表的第一个结点。
*引入头结点的两个优点:
(1)由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作就和在表的其它位置上操作一致,无须进行特殊处理
(2)无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域空),因此空表和非空表的处理也就统一了
单链表基本操作的实现:
单链表的建立1(头插法):
时间复杂度O(n)
voidCreateList1_L(LinkList&L,intn){
//从表尾到表头逆位序输入n个元素的值,建立带头结点的单链表L
//每次均在头结点之后插入元素
L=(LinkList)malloc(sizeof(LNode));//创建头结点
L->next=NULL;//初始为空表
for(i=n;i>0;i--){
p=(LinkList)malloc(sizeof(LNode));//创建新结点
scanf(&p->data);
p->next=L->next;
L->next=p;//将新结点插入表中
}
}//CreateList1_L
单链表的建立2(尾插法):
时间复杂度O(n)
voidCreateList2_L(LinkList&L,intn){
//从表头到表尾正向输入n个元素的值,建立带头结点的单链表L
//每次均在表尾插入元素
L=(LinkList)malloc(sizeof(LNode));//创建头结点
L->next=NULL;//初始为空表
q=L;//表尾指针
for(i=n;i>0;i--){
p=(LinkList)malloc(sizeof(LNode));//创建新结点
scanf(&p->data);
r->next=p;
r=p;//将新结点插入表中
}
r->next=NULL;//表尾指针置空
}//CreateList2_L
单链表的查找1(按位置查找):
LNode*GetElem_L(LinkListL,inti){
//L为带头结点的单链表的头指针
//当第i个元素存在时,返回第i个结点的指针,否则返回NULL
intj=1;//计数,初始为1
p=L->next;//初始化,p指向每一个结点
while(p&&j
p=p->next;
j++;
}
if(!
p||j>i)returnNULL;//若i大于表长,p=NULL直接返回NULL
returnp;//返回第i个结点的指针
}//GetElem_L
单链表的查找2(按值查找):
LNode*LocateElem_L(LinkListL,ElemTypee){
//L为带头结点的单链表的头指针
//当值查找成功时返回数据域值等于e的结点指针,否则返回NULL
inti=0;
p=L->next;
while(p&&p->data!
=e){//顺时针向后查找,直到p指向值为e的元素或p为空p=p->next;
}
returnp;//找到后返回该结点指针,否则返回NULL
}//LocateElem_L
插入操作(前插):
先找到前驱结点*p,然后完成在*p后插入*s,时间复杂度T(n)=O(n)
BOOLListInsert_L(LinkList&L,inti,ElemTypee){
//在带头结点的单链表L中第i个位置插入元素e
p=GetElem_L(L,i-1);//查找插入位置的前趋结点
if(!
p)returnFALSE;//插入位置不合法
s=(LinkList)malloc(sizeof(LNode));//生成新结点
s->data=e;//插入L中
s->next=p->next;
p->next=s;
returnTRUE;
}//ListInsert_L
插入操作(后插):
设p指向单链表中某结点,s指向待插入值为e的新结点,将*s插入到*p之后。
时间复杂度T(n)=O
(1)
//只给出关键代码段
s->next=p->next;//修改指针域,顺序不能颠倒
p->next=s;
e=p->data;//交换数据域部分
p->data=s->data;
s->data=e
删除操作1:
先找到被删结点的前趋,然后完成在*p后删除被删结点,时间复杂度T(n)=O(n)
BOOLListDelete_L(LinkList&L,inti,ElemType&e){
//在带头结点的单链表L中,删除第i个元素,并由e返回其值
p=GetElem_L(L,i-1);//查找第i个结点,并令p指向其前趋
if(!
p)returnFALSE;//删除位置不合法
q=p->next;//令q指向被删除结点
p->next=q->next;//将*q结点从链中断开
e=q->data;//取其数据域的值
free(q);//释放结点的存储空间
returnTRUE;
}//ListDelete_L
删除操作2:
将结点*p后继结点的值赋予其自身,然后删除后继结点,时间复杂度T(n)=O
(1)
//只给出关键代码段
q=p->next;//修改指针域,顺序不能颠倒
p->data=p->next->data;//和后继结点交换数据域
e=p->data;
p->next=q->next;//将*q结点从链中断开
free(q);//释放结点的存储空间
单链表的归并:
voidMergeList_L(LinkListLa,LinkListLb,LinkList&Lc){
//已知单链表La和Lb的元素按值非递减排列
//归并La和Lb得到新的单链表Lc,Lc的元素也按值非递减排列
pa=La->next;
pb=Lb->next;
Lc=pc=La;//用La的头结点作为Lc的头结点
while(pa&&pb){//开始归并
if(pa->data<=pb->data){
pc->next=pa;
pc=pa;
pa=pa->next;
}
else{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?
pa:
pb;//插入剩余段
free(Lb);//释放Lb头结点
}//MergeList_L
(3)各种变形链表(循环链表、双向链表、带头结点的链表等)的表示和基本操作的实现;
循环链表:
循环单链表:
表尾结点的next域指向L,表中没有指针域为NULL的结点,因此循环单链表的判空条件是看它的头结点的指针域是否等于头指针;循环单链表在任何一个位置上插入和删除操作都是等价的,无需判断是否表尾;循环单链表可以从表中任一结点开始遍历整个链表。
循环单链表不设头指针而只设尾指针,可使一些操作效率更高。
循环双链表:
与循环单链表不同的是,循环双链表中头结点的prior指针还指向表尾结点;在循环双链表L中,某结点*p为尾结点时,p->next==L;当循环双链表为空时,对其头结点*p,有p->prior=p->next==L。
双向链表:
其结点有两个指针域,一个指向直接后继,另一个指向直接前趋,双向链表的插入、删除结点算法的时间复杂度为O
(1)。
双向链表结构的定义:
typedefstructDuLNode{
ElemTypedata;
structLNode*prior,*next;
}DuLNode,*DuLinkList;
双向链表的插入操作:
//只给出关键代码段
//在结点*p之后插入值为e的新结点*s
s->data=e;
s->next=p->next;
p->next->prior=s;//语句执行顺序不唯一,但这两步必须在最后一步之前
s->prior=p;
p->next=s;
//在结点*p之前插入值为e的新结点*s
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
双向链表的删除操作:
//只给出关键代码段
//删除结点*p的后继结点*q,并用e返回其值
e=q->data;
p->next=q->next;
q->next->prior=p;
free(q);
//删除结点*q的前趋*p,并用e返回其值
e=p->data;
q->prior=p->prior;
p->prior->next=q;
free(p);
带头结点的链表:
带头结点的链表结构定义:
typedefstructLNode{//结点类型
ElemTypedata;
structLNode*next;
}*Link,*Position;
typedefstruct{//链表类型
Linkhead,tail;//分别指向链表的头结点和尾结点
intlength;//线性表中元素的个数
}LinkList;
ch3.栈与队列
(1)栈和队列的基本概念;栈和队列的顺序存储结构、链式储存结构及其存储特点;
栈:
限定在表尾进行插入或删除操作(头进尾出)的线性表,表尾称为栈顶(top),表头称为栈底(bottom),不含任何元素的栈称为空栈,栈又被称为后进先出(LIFO)线性表。
队列:
只允许在表的一端进行插入,另一端进行删除操作(尾进头出),允许插入的一端叫做队尾(rear),允许删除的一端叫做队头(front),是一种先进先出(FIFO)的线性表。
栈的基本操作:
InitStack(&S)初始化,构造一个空栈S
StackEmpty(S)栈S判空
StackLength(S)求栈长,即栈S的元素个数
Push(&S,x)入栈,插入元素为x的新栈顶元素
Pop(&S,x)出栈,删除栈S的栈顶元素,并用x返回其值
GetTop(S,&x)取栈顶,用x返回栈S的栈顶元素
ClearStack(&S)将栈S清空
DestroyStack(&S)销毁栈,并释放其存储空间
栈的顺序存储(顺序栈):
可能发生上溢
基本操作:
//只给出关键语句
InitStack(&S)//栈满条件S.top-S.base=S.StackSize
S.top=S.base;S.StackSize=InitSize;//栈空条件S.top==S.base
GetTop(S,&e)
e=*(S.top-1);
Push(&S,e)
*S.top++=e;//入栈时栈顶指针先加1,再送值到栈顶元素
Pop(&S,&e)
e=*--S.top;//出栈时先取栈顶元素值,再将栈顶指针减1
栈的链式存储(链栈):
便于多个栈共享存储空间和提高其效率,不存在栈满上溢的情况,规定链栈没有头结点,Lhead指向栈顶元素。
基本操作:
//只给出关键语句
InitStack(&S)//初始化
p->next=S->next;S->next=p;
Push(&S,e)//入栈
p->data=e;S->next=p;p->next=S->next;
Pop(&S,&e)//出栈
p=S->next;e=p->data;S->next=p->next;free(p);
DestroyStack(&S)//销毁栈
p=S;S=S->next;free(p);
队列的链式存储(链队列):
基本操作:
//只给出关键语句
InitQueue(&Q)//初始化
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));Q.front->next=NULL;
DestroyQueue(&Q)//销毁队列
Q.rear=Q.front->next;Free(Q.front);Q.front=Q.rear;
EnQueue(&Q,e)//入队
p->data=e;p->next=NULL;Q.rear->next=p;Q.rear=p;
DeQueue(&Q,&e