所得到的结点地址(下标)存入 t 中;不难看出,当 AV 表非空时,摘下了第一个结点 给用户。当用户不再需要某个结点时,需通过该结点的相对地址 t 将它还给 AV,交给 AV 表 的结点链在了 AV 的头部,而不能调用系统的 free()函数。相关语句为:
sd[t].next=AV;
AV=t;
2.4.6 案例分析
案例 4
问题:链表基本算法的实现。
算法分析:编写链表基本操作函数,实现下面的操作:
(1)初始化一链表。
(2)在主函数中建立一选项菜单,使用户可选择进行插入、查找、删除或退出等操作。
data next
SL=0 0 4
1 a4 5
2 a2 3
3 a3 1
4 a1 2
5 a5 -1
AV=6 6 7
7 8
8 9
9 10
10 11
11 -1
表 SL
表 AV
(3)调用插入函数建立一个链表。
(4)查找用户从键盘输入的指定值的元素,如果该元素存在,返回该元素值在链表中的 第一个位置;否则返回指定值不存在的提示。
(5)查找用户从键盘输入的指定位置的元素,如果该位置合适,返回该位置的元素值;
否则返回指定位置不合理的提示。
(6)删除用户从键盘输入的指定值的元素,如果该元素值存在,返回删除成功的提示,
否则返回用户所输入元素值不存在的提示。
(7)删除用户从键盘输入的指定位置的元素,如果指定位置合适,返回删除成功的提示,
否则返回用户所输入的位置不合适的提示。
(8)遍历并输出顺序表中的元素。
(9)在进行插入、查找、删除等操作后,必须及时输出链表中的元素,便于观察操作结果。
算法实现:
#include<stdio.h>
#include<alloc.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
void InitLinkList(LinkList *p) {
*p=NULL;
}
void InsertLinkList(LinkList *p,int elem,int j) {
int i;
LinkList u,q,r;
u=malloc(sizeof(LNode));
u->data=elem;
for(i=0,r=*p;i<j && r!=NULL;i++) {
q=r;
r=r->next;
} if(r==*p) *p=u;
else
q->next=u;
u->next=r;
}
int DeleteLinkList(LinkList *p,int elem) { LinkList q,r;
q=*p;
if(q->data==elem)
{*p=q->next;
free(q);
return 0;
}
for(;q->data!=elem && q!=NULL; r=q, q=q->next);
if(q==NULL) return 1;
if(q->data==elem) {r->next=q->next;
free(q);
return 0;
}
else return 1;
}
int FindLinkList(LinkList p,int elem) {
int i;
for( i=1;(p->data!=elem) && p!=NULL; p=p->next, i++);
return(p==NULL)? -1:i;
}
void OutputLinkList(LinkList p) { LinkList q=p;
printf("链表为:\n");
while(q!=NULL) {
printf("%4d",q->data);
q=q->next;
}
printf("\n");
}
void main() {
LinkList p;
int op,i,j,r;
InitLinkList(&p);
while(1) {
printf("选择操作 1:在指定位置插入元素 2:查找指定的元素\n");
printf("3:删除指定的元素 0:Exit\n");
fflush(stdin);
scanf("%d",&op);
switch(op) {
case 0:
return;
case 1:
printf("输入要插入的元素值及位置:");
算法分析:依次取原链表中的每个结点,将其作为第一个结点插入新链表,指针 p 用来 指向当前结点,p 为空时结束。
算法实现:
void reverse (Linklist H) { LNode *p;
p=H->next; //p 指向第一个数据结点 H->next=NULL; //将原链表置为空表 H while (p)
{ q=p; p=p->next;
q->next=H->next; //将当前结点插到头结点的后面 H->next=q;
} }
算法 2.15
该算法只是对链表顺序扫描一遍即完成了倒置,时间性能为 O(n)。
案例 6
问题:已知单链表 L,写一算法,删除其重复结点,即实现如图 2.24 所示的操作。图 2.24
(a)所示为删除前,(b)所示为删除后。
图 2.24 删除重复结点 算法分析:
用指针 p 指向第一个数据结点,从它的后继结点开始到表的结束,找与其值相同的结点 并删除之;p 指向下一个;依此类推,p 指向最后结点时算法结束。
算法实现:
void pur_LinkList(LinkList H) { LNode *p,*q,*r;
p=H->next; //p 指向第一个结点 if(p==NULL) return;
while (p->next) { q=p;
while (q->next) //从*p 的后继开始找重复结点 { if (q->next->data==p->data)
{ r=q->next; //找到重复结点,用 r 指向,删除*r q->next=r->next;
free(r);
} //if else q=q->next;
10 ∧
15 15 18
H 10
(a)
18 ∧
10 15 H
(b)
} //while(q->next)
p=p->next; //p 指向下一个结点,继续 } //while(p->next)
}
算法 2.16
该算法的时间性能为 O(n2)。
*案例 7
问题:在带头结点的静态链表 SL 的第 i 个结点之前插入一个值为 x 的新结点。
算法分析:
可设静态链表的存储区域 sd 为全局变量,指针是结点为数组的下标。
算法实现:
int Insert_SList( int SL, datatype x, int i) { int p,s;
p=SL; j=0;
while(sd[p].next!=-1 && j<i-1)
{p=sd[p].next;j++;} //找第 i-1 个结点 if(j==i-1)
{ if(AV!=-1) //若 AV 表还有结点可用 {t=AV;
AV=sd[AV].next; //申请、填装新结点 sd[t].data=x;
sd[t].next=sd[p].next; //插入 sd[p].next=t;
return 1; //正常插入成功返回 }
else{printf("存储池无结点");return 0;}
//未申请到结点,插入失败 else{printf("插入的位置错误");return -1;}
//插入位置不正确,插入失败 }
算法 2.17
读者可将该算法和算法 2.12 进行比较,除了一些描述方法有些区别外,算法思路是相同的。