1 线性表
定义
由零个或多个数据元素组成的有限序列。
说明:
(1)线性表是一个序列,即元素之间是由个先来后到的。
(2)如果元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他元素都有且只有一个前驱和后继。
(3)线性表是有限个的。
用数学语言来进行定义,将线性表记为(a1,…,ai-1,ai+1,an),则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。
线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。
2 抽象数据类型
2.1 数据类型
原子类型:整型、浮点型、字符型等
结构体类型:由若干个类型组合而成,是可以再分解的。例如:
抽象是指取出事务具有的普遍性的本质。抽象数据类型可以是编程者在设计软件程序时自己定义的数据类型。
ADT 抽象数据类型名{
数据对象:<数据对象的定义>
数据关系:<数据关系的定义>
基本操作:<基本操作的定义>
}ADT 抽象数据类型名
数据对象:{a1,a2,…,an},任意数据元素的集合。
数据关系:第一个元素无前驱,最后一个元素无后继,其他元素都有且只有一个前驱和后继。
基本操作:抽象数据类型List的7个基本运算和结构初始化运算的接口如下:
List ListInit(int size)/*表结构初始化*/
int ListEmpty(List L)/*测试表L是否为空*/
int ListLength(List L);//表L的长度
ListItem ListRetrieve(int k,List L);
int ListLocate(ListItem x, List L);/*元素x在表L中的位置*/
void ListInsert(int k,ListItem x,List L);/*在表L的位置k之后插入元素x*/
ListItem ListDelete(int k,List L);/*从表L中删除位置k处的元素*/
void PrintList(List L);/*按位置次序输出表L中元素*/
这里主要讨论元素的插入和删除运算的实现。
表元素插入运算ListInsert(k,x,L):
void ListInsert(int k,ListItem x,List L)
{
if(k<0 || k>L->n) exit(1);
for(int i=L->n-1;i>=k;i--) L->table[i+1]=L->table[i];
L->table[k]=x;
l->n++;
}
分析:
(1)在第4行将表L位于k+1,k+2,……,n处的元素分别移到位置k+2,k+3,……,n+1处,然后早第5行将新元素插入位置k+1处。
(2)算法中元素后移的方向,必须从表中最后一个位置开始后移,直至将位子k+1处的元素后移。
(3)算法的时间复杂性,平均时间为o(n)。
表元素删除运算ListDelete(k,L):
ListDelete(int k,List L)
{
if(k<1 || k>L->n) exit(1);
ListItem x=L->table[k-1];
for(int i=k;i<L->n;i++) L->table[i-1]=L->table[i];
L->n--;
return x;
}
分析:
(1)通过将表L位于k+1,k+2,……,n处的元素移到位置k,k+1,……,n-1来删除原来位置k处的元素。
(2)最好情况下需要O(1)时间,而在最坏情况下需要O(n)时间。
删除运算所需的平均时间为O(n)。
3 用指针实现表
核心:让每个单元包含一个元素和一个指针,其中指针指向表中下一个元素所在的单元。这就是单链接表,简称单链表或链表。单链表的逻辑结构如下图。
3.1 单链表的结点结构说明
编写程序:
typedef struct node *link;//表节点指针类型
type struct node{
ListItem element;//表元素
link next;//指向下一结点的指针
}Node;
link NewNode()
{
return (link)malloc(sizeof(Node));
}
分析:
(1)ListItem表示用户指定的元素类型。其数据成员element存储表中元素。
(2)next是指向表中下一个元素的指针。函数NewNode()用于产生一个新结点。
(3)定义用指针实现表的结构List如下。
typedef struct llist *List;//单链表指针类型
typedef struct llsit{
link first,//链表首指针
curr,//链表当前结点指针
last;//链表尾指针
}List;
分析:
(1)表结构List的数据成员first是指向表中第1个元素的指针。
(2)当表为空表时first指针是空指针。
3.2 函数ListInit()
编写程序:
使用“List ListInit(int size)//表结构初始化”,创建一个空表。
List ListInit()
{
List L=(List)malloc(sizeof *L);
L->first=0;
return L;
}
3.3 函数ListEmpty(L)
函数ListEmpty(L)测试当前表L是否为空,只要看表首指针first是否为空指针。
编写程序:
int ListEmpty(List L)/*测试表L是否为空*/
{
return L->first==0;
}
3.4 函数ListLength(L)
函数ListLength(L)对表L进行线性扫描计算表的长度。
编写程序:
int ListLength(List L){
//表L的长度
int len=0;
link p=L->first;
whiule(p){
len++;
p=p->next;
}
return len;
}
算法ListLength(L)需要O(n)计算时间。
3.5 函数ListRetrieve(k,L)
编写程序:
ListItem ListRetrieve(int k,List L)
{
if(k<1) exit(1);
link p=L->first;
int i=1;
while(i<k&& p){
p=p->next;
i++;
}
return p->element;
}
函数ListRetrieve(k,L)从表首开始逐个元素向后线性扫描,直至找到表L中第k个元素。算法ListRetrieve(k,L)需要O(k)计算时间。
3.6 函数ListLocate(x,L)
编写程序:
int ListLocate(ListItrm x,List L)
{
int i=1;
link p=L->first;
while(p&& p->element!=x){
p=p->next;
i++;
}
return p?1:0;
}
从表首开始逐个元素向后线性扫描,直至找到表L中元素x。在最坏情况下,算法ListLocate(x,L)需要O(n)需要计算时间。
3.7 单链表L插入元素
在单链表L中位置k处插入1个元素x的算法ListInsert(k,x,L)可实现如下
编写程序:
void ListInsert(int k,ListItem x,List L)
{
if(k<0) exit(1);
link p=L->first;
for(int i=1;i<k&&p;i++) p=p->next;
link y=NewNode();
y->element=x;
if(k){
y->next=p->next;p->next=y;}
else{
y->next=L->first;L->first=y;}
}
分析:
首先扫描表找到插入位置k处的结点p,然后建立一个存储待插入元素x的新结点y,最后将结点y插入到结点p之后。
算法ListInsert(k,x,L)所需时间为O(k)。
3.8 在单链表删除元素
算法ListDelete(k.L)处理以下3种情况:
一,k<1或链表为空;二,删除的是表首元素,即k=1;三,删除非表首元素,即k>1。
遇到情况一,则表中不存在第k个元素,给出越界信息;遇到情况二,则直接修改首指针first,删除表首元素;三,则先找到表中第k-1元素所在结点q,然后修改结点q的指针域,删除第k个元素所在的结点p,如下图
编写程序:
ListItem ListDelete(int k,List L)
{
if(k<1 || !L->first) exit(1);
link p=L->next;
if(k==1) L->first=p->next;
else{
link p=L->first;
for(int i=1;i<k-1 && q;i++) q=q->next;
p=q->next;
q->next=p->next;
}
ListItem x=p->element;
free(p);
return x;
}
算法ListDelete(k,L)所需计算时间为O(k)。
3.9 函数PrintList(List L)
编写程序:
void PrintList(List L)
{
for(link p=L->first;p;p=p->next) ItemShow(p->element);
}
分析:算法PrintList输出表L中所有元素。
4 用间接寻址方法实现表
间接寻址方法试将数组和指针结合起来实现表的一种方法,它将数组中原来存储元素的地方改为存储指向元素的指针。
4.1 用间接寻址方法实现表的结构
编写程序:
typedef struct indlist *List;//表指针类型
typedef struct indlist;//表结构类型
int n,//表长
curr;//当前位置
int maxsize;//数组上界
addr *table;//存储表元素指针的数组
}Indlist;
分析:
addr是表元素指针类型,已经在ListItem中定义。n为表长,maxsize是指针数组的最大长度,table是指向表中元素的指针数组。
4.1 NewNode()产生一个新节点
编写程序:
addr NewNode()
{
return (addr)malloc(sizeof(addr));
}
4.3 函数ListInit(size)
编写程序:
List ListInit(int size)
{
List L=(List)malloc(sizeof *L);
L->n=0;
L->maxsize=size;
L->table=(addr *)maddloc(size*sizeof(addr));
return L;
}
分析:创建一个最大长度为size的空表
4.4 表运算ListEmpty(L)和ListLength(L)
编写程序:
int ListEmpty(List L)
{
return L->n==0;
}
int ListLength(List L){
return L->n;
}
分析:表运算ListEmpty(L)和ListLength(L)只需O(1)计算时间。
4.5 表运算ListRetrieve(k,L)和 ListLocate(x,L)
ListRetrieve(k,L)
编写程序:
ListItem ListRerieve(ListItem x.List L)
{
for(int i=0;i<L->n;i++)
if(*L->table[i]==x) return ++i;
return 0;
}
分析:
ListRetrieve(k,L)返回表L的位置k处的元素。表L中没有位置k时,给出错误信息,算法ListRetrieve(k,L)只需O(1)计算时间。
ListLocate(x,L)
编写程序:
int ListLocatte(ListItem x,List L)
{
for(int i=0;i<L->n;i++)
if(*L->table[i]==x) return ++i;
return 0;
}
分析:
ListLocate(x,L)返回元素x在表L中的位置,当元素x不在表L中是返回0。算法ListLocate(x,L)在最坏情况下需要O(n)计算时间。
4.6 用间接寻址方法实现表元素的插入运算
表元素插入运算ListInsert(k,x,L)实现如下。
编写程序:
void ListInsert(int k,ListItem x,List L)
{
if(k<0 || k>L->n) exit(1);
for(int i=L->n-1;i>=k;i--) L->table[i+1]=L->table[i];
L->table[k]=NewNode();
*L->table[k]=x;
L->n++;
}
分析:
(1)核心:不实际移动元素,而只移动指向元素的指针。
(2)算法ListInsert(l,x,L)将位于k+1,k+2,……,n处的元素指针分别移动位置k+2,k+3,……,n+1处,然后将指向新元素x的指针插入位置k+1处。
(3)该算法计算时间为O(k)。但该算法比用数组实现表的插入算法快的多。
4.7 用间接寻址方法实现表元素删除运算
表元素删除运算ListDelete(k,L)实现如下。
编写程序:
ListItem ListDelete(int k,List L)
{
if(k>1 || k>L->n) exit(1);
addr p=L->table[k-1];
ListItem x=*p;
for(int i=k;i<l->n;i++) L->table[i-1]=L->table[i];
L->n--;
free(p);
return x;
}
分析:
(1)核心:不实际移动元素,而只移动指向元素的指针。
(2)算法ListInsert(l,x,L)将位于k+1,k+2,……,n处的元素指针移到位置k,k+1,……,n-1处来删除原来位置k处的元素。
(3)该算法计算时间为O(k)。但该算法比用数组实现表的插入算法快的多。
4.8 用间接寻址方法输出表所有元素
输出表所有元素的函数PrintList(L)
编写程序:
void PrintList(List L)
{
for(int i=0;i<L->n;i++)ItemShow(*L->table[i]);
}
5 实现游标对指针的模拟
游标是数值中指示数组单元地址的下标值,属于整数类型。用数组和指针相结合,并用游标,模拟指针的方法来实现表。
5.1 数组单元类型Snode定义
typedef struct snode *link;//结点指针类型
typedef struct snode{
ListItem element;//表中元素
int next;//模拟指针的游标
}
分析:
(1)element域存储表中元素;next是用于,模拟指针的游标,它指示表中下一个元素再数组中的存储地址。
(2)用游标模拟指针可方便实现单链表中的各种运算。
5.2 定义模拟空间结构类型Space
目的:实现游标对指针的模拟,必须先设计模拟内存管理的结点空间分配与释放运算、模拟C语言的函数malloc和free。
typedef struct space *Space;
typedef struct space{
int num,//可用数组空间大小
first;//可用数组单元小标
link node;//可用空间数组
}Simul;
分析:
(1)数据成员num表示可用数组空间大小
(2)node[0:num]是供分配的可用数组,初始时所有单元均可分配
5.3 函数SpaceInit(int max)
Space SpaceInit(int max)
{
Space s=(Space)malloc(sizeof*s);
s->num=max;
s->node=(link)malloc(max*(sizeof*s->node));
for(int i=0;i<max-1;i++) s->node[i].next=i+1;
s->node[max-1].next=-1;
s->first=0;
return s;
}
分析:函数SpaceInit(max)创建一个可用数组空间最大长度为max的模拟空间结构。所需的计算时间O(num)。
5.4 函数SpaceAllocate(s)
int SpaceAllocate(Space s)
{
int i=s->first;
s->first=s->node[i].next;
return i;
}
分析:从s的当前可用数组空间中分配一个数组单元的函数SpaceAllocate(s)。所需的计算时间为O(1)。
5.5 函数SpaceDeallocate(i,s)
void SpaceDeallocate(int i,Space s){
s->node[i].next=s->first;
s->first=i;
}
分析:释放s的数组单元i的函数SpaceDeallocate(i,s),所需的计算时间为O(1)。
6 双可用空间表方法实现游标对指针的模拟
用可用空间表表示当前可用数组空间。其中,第一个可用空间表中含有所有未用过的可用数组单元;第2个可用空间表中含有所有至少被用过1次且已被释放的可用数组单元。SpaceDeallocate释放的所有单元均链如第2个可用空间表中备用。SpaceAllocate在分配1个可用数组单元时,总是先从第2个可用空间表中获取可用数据单元。
6.1 定义双模拟空间结构类型
在双可用空间表中用first指向第一个可用空间表的表首可用数组单元,用first2指向第2个可用空间表的表首可用数组单元。
typedef struct dspace *Space;//双模拟空间指针类型
typedef struct dspace{
//双模拟空间结构类型
int num,//可用数组空间大小
first1,//第1个可用空间表的表首可用数组单元下标
first2,//第2个可用空间表的表首可用数组单元下标
link node;//可用空间数组
}Dspace;
6.2 函数SpaceInit(max)
创建初始可用数组空间的函数得到简化如下:
Space SpaceInit(int max)
{
Space s=(Space)malloc(sizeof *s);
s->num=max;
s->node=(link)malloc(max*(sizeof *s->node));
s->first1=0;
s->first2=-1;
return s;
}
6.3 函数SpaceAllocate(s)
从当前可用数组空间中分配一个数组单元SpaceAllocate(s)相应修改如下。
int SpaceAllocate(Space s)
{
if(s->first2==-1) return s->first1++;
int i=s->first2;
s->first2=s->node[i].next;
return i;
}
6.4 函数函数SpaceDeallocate(i,s)
释放数组单元i的函数后SpaceDeallocate(i,s)也进行相应修改如下。
void SpaceDeallcate(int i,Space s)
{
s->node[i].next=s->first2;
s->first2=i;
}
7 用游标实现表
7.1 用游标实现的表结构
表结构List
typedef struct slist *List;//游标表指针类型
typedef struct slist{
//游标表结构
int first,//表首结点游标
Space s;
}Slist;
分析:
List的数据成员first是表首结点游标;s表示可用数组空间。
7.2 函数ListInit(size)
List ListInit(int size)
{
List L=(List)malloc(sizeof *L);
L->s=SpaceInit(size);
L->first=-1;
return L;
}
分析:函数ListInit(size)申请大小为size的模拟空间,并置表首结点游标first为-1,创建一个空表。
7.3 函数ListLength(L)
int ListLength(List L)
{
int i=L->first,len=0;
while(i!=-1){
len++;
i=L->s->node[i].next;
}
return len;
}
分析:函数ListLength(L)对表L进行线性扫描来计算表的长度。
7.4 函数ListRetrieve(k,L)
ListItem ListRetrieve(int k,List L)
{
int p,i=1;
if(k<1) exist(1);
p=L->first;
while(i<k && p!=-1){
p=L->s->node[p].next;
i++;
}
return L->s->node[p].element;
}
7.5 函数ListLocate(x,L)
int ListLocate(ListItem x,List L)
{
int p,i=1;
p=L->first;
while(p!=-1 && L->s->ndoe[p].element!=x){
p=L->s->node[p].next;
i++;
}
return ((p>=0)?i:0);
}
分析:从表首开始逐个元素向后进行线性扫描,直到扎到表L中元素x。在最坏情况下,它需要O(n)计算时间。
7.6 插入表元素
思路:要在当前表的第k个元素之后插入一个新元素x,应先找到插入位置,即当前表的第k个元素所处的位置;然后从可用数组空间中为新元素分配一个存储单元,并将由此产生的新元素插入表的第k个元素之后。
void ListInsert(int k,ListItem x,List L)
{
if(k<0) exit(1);
int p-L->List;
for(int i=1;i<k && p!=-1;i++) p=L->s->node[p].next;
int y=SpaceAllocate(L->s);
L->s->node[y].element=x;
if(k){
L->s->node[y].next=L->s->node[p].next;
L->s->node[p].element=y;
}
else{
L->s->node[y].next=L->first;
L->first=y;
}
}
分析:以上结构未使用表首哨兵单元,所以碧玺单独处理在表的第一个位置插入的情形。算法ListInsert(k,x,L)的计算时间为O(k)。
7.7 删除表元素
思路:先找到当前表的第k个元素所处的位置;然后将存储该元素的那个单元摘除,并把该单元释放到可用数组空间中备用。
ListItem ListDelete(int k,List L)
{
if(k<1 || L->first==-1) exit(1);
int p=L->first;
if(k==1) L->first=L->s->node[p].next;
else{
int q=p;
for(int i=1;i<k-1 && q!=-1;i++) q=L->s->node[q].next;
p=L->s->node[q].next;
L->s->node[q].next=L->s->node[p].next;
}
ListItem x=L->s->ndoe[p].element;
SpaceDeallocate(p,L->s);
return x;
}
分析:算法ListDelete(k,L)所需计算时间为O(k)。
7.8 函数PrintList(L)
void PrintList(List L)
{
for(int p=L->first;p!=-1;p=L->s->ndoe[p].next)
ItemShow(L->s->ndoe[p].element);
}
分析:输出表中所有元素的函数PrintList(L)。
8 循环链表
在用指针实现表时,表中最后一个元素所在单元的指针为空指针,如果将这个空指针改为指向表首单元的指针,就使整个链表形成一个环。这种首尾相接的链表就称为循环链表。
在循环链表中,从任意一个单元出发都可以找到表中其他单元。
一个单链的循环链表,简称单循环链表。
8.1 单循环链表的结构
typedef struct clsit *List;//单循环链表指针类型
typedef struct clsit{
//但循环链表结构
int n;//表长
link last;//链表尾指针
}Clist;
8.2 函数ListInit()
创建一个空表。
List listInit()
{
List L=(List)malloc(sizeof *L);
link y=NewNode();
y->next=y;
L->last=y;
L->n=0;
return L;
8.3 函数ListRetrieve(k,L)
从表中第1个元素开始逐个向后扫描,直至找到第k个元素。
ListItem listRetrieve(int l,List L)
{
int i=1;
if(k<1 || k>L->n) exit(1);
link p=L->last->next->next;
while(i<k){
p=p->next;
i++;
}
return p->element;
}
分析:函数ListRetrieve(k,L)需要O(k)计算时间。
8.4 函数ListLocate(x,L)
从表中第一个元素逐个想后线性扫描,直至找打元素x。在最坏情况下,算法ListLocate(x,L)需要O(n)计算时间。
int ListLocate(ListItem x,List L)
{
int i=1;
link p=L->next->next;
L->last->next->element=x;
while(p->element!=x)
p=p->next;
i++;
}
return ((p==L->last->next)?0:i);
}
8.5 函数ListInsert(k,x,L)
在训话链表中插入一个新元素的算法与单链表类似。采用表首哨兵单元,简化了算法对表首插入的边界情形的处理。
void ListInsert(int k,ListItem x,List L)
{
if(k<0 || k>L->n) exit(1);
link p=L->next->next;
link y=NewNode();
y->element=x;
y->next=p->next;
p->next=y;
if(k==L->n) L->last=y;
L->n++;
}
算法ListInsert(k,x,L)所需计算时间为O(k)。
8.6 函数ListDelete(k,L)
在循环链表中刷除第k个元素的算法也与单链表的情形类似。同样,表首哨兵单元的采用,简化了算法对删除表中第1个元素的边界情形的处理。
ListItem ListDelete(int k,List L)
{
if(k<1||k>L->n) exit(1);
link q=L->last->next;
for(int i=0;i<k-1;i++) q=q->next;
link p=q->next;
q->next=p->next;
if(k==L->n) L->last=q;
ListItem x=p->element;
free(p);
L->n--;
return x;
}
8.7 函数PrintList(L)
输出表中所有元素。
void PrintList(List L)
{
for(link p=L->last->next->next;p!=L->last->next;p=p->next)
ItemShow(p->element);
}
9 双链表
如果希望快速确定表中任一元素的前驱和后继所在的结点,可用在链表的每个结点中设置两个指针,一个指向后继节点,另一个指向前驱结点,形成双向链表,简称为双链表。
9.1 双链表结点类型定义
typedef struct node *link;//双链表结点指针类型
typedef struct node{
//双链表结点类型
ListItem elemnet;//表中元素
link left,//链表做结点指针
right;//链表右结点指针
}Node;
分析:数据成员element存储表中元素;left是指向前一结点的指针;right是指向后一结点的指针。
9.2 双链表实现表的结构类型定义
typedef struct dlist *List;//双链表结点指针类型
typedef struct dlist{
//双链表结点类型
int n;//表长
link leftEnd,//链表做结点指针
rightEnd;//链表右结点指针
}Dlist;
分析:数据成员n存储表的长度;leftEnd是指向表首的指针;rightEnd是指向表尾的指针。
拓展:双链表也可以有循环表。用一个表首哨兵header将双联表表首尾想接,即将表首哨兵节点中的left指针指向表尾,并将表尾结点的right指针指向表首哨兵结点,构成双向循环链表。
9.3 双向循环链表实现表的结构类型定义
typedef struct dlist *List;//循环双链表指针类型
typedef struct dlist;//循环双链表结构
int n;//表长
link head,//链表表首指针
curr;//链表当前结点指针
}Dlist;
head是指向表首哨兵结点的指针。
9.4 函数ListInit()
创建一个仅由表首哨兵结点组成的空双向循环链表。
List ListInit()
{
List L=(List)malloc(sizeof *L);
link y=NewNode();
y->left=y;
y->right=y;
L->header=y;
L->n=0;
return L;
}
9.5 函数ListRetrieve(k,L)
从表L中第一个元素开始逐个向后扫描,直至找到第k个元素。
ListItem ListRetrieve(int k,List L)
{
if(k<1 || k>L->n) exit(1);//越界
if(k==L->n) return L->header->left->element;
link p=L->header->right;
for(int i=1;i<k;i++) p=p->right;
return p->element;
}
由于双向循环链表的特点,计算时间为O(1)。
9.6 函数ListLocate(x,L)
从表中第1个元素开始逐个向后进行线性扫描,直至找到元素x。在最坏情况下,算法ListLocate(x,L)需要O(n)时间。
int ListLocate(ListItem x,List L)
{
int i=1;
link p=L->header->right;
L->header->element=x;
while(p->element!=x)
p=p->right;
i++;
}
return ((p==L->header)?0:i);
}
9.7 双向循环链表插入运算
修改向前和向后两个方向的指针
在双向循环链表的第k个元素之后插入一个新元素x。
void ListInsert(int k,ListItem x,List L)
{
if(k<0 || k>L->n) exit(1);
link p=L->header;
if(k==L->n) p=L->header->left;
else for(int i=1;i<=k;i++) p=p->right;
link y=NewNode();
y->element=x;
y->left=p;
y->right=p->right;
p->right->left=y;
p->right=y;
L->n++;
}
由双向循环链表的特点,计算时间为O(1)。
9.8 双向循环链表删除运算
删除双向循环链表中第k个元素的算法实现。
List ListDelete(int k,List L)
{
if(k<0 || k>L->n) exit(1);
link p=L->header;
if(k==L->n) p=L->header->left;
else for(int i=1;i<=k;i++) p=p->right;
p->left->right=p->right;
p->right->left=p->left;
ListItem x=p->element;
free(p);
L->n--;
return x;
}
分析:算法ListDelete(k,L)所需计算时间O(k)。
9.9 输出双向循环表中所有元素
用游标来模拟指针,实现用游标表示的双向链表和循环链表。
输出双向循环链表中所有元素的。
void PrintList(List L)
{
for(link p=L->header->right;p!=L->header;p=p->right)
ItemShow(p->element);
}
10 表的搜索游标
在对表进行各种操作时,常需要对表进行顺序扫描为了使这种顺序扫描具有通用性,可以将与之相关的运算定义为抽象数据类型表的基本运算,常用的有如下几种。
(1)IterInit(L):初始化搜索游标。
(2)IterNext(L):当前搜索游标的下一个位置。若当前搜索游标在表尾,则下一个位置为表首。
(3) CurrItem(L):当前搜索游标处的表元素。
(4)InsertCurr(x, L):在当前搜索游标处插入元素x。
(5) DeleteCurr(L) :删除当前搜索游标处的元素。
10.1 用数组实现表的搜索游标
在用数组实现的表结构中增加一个数据成员curr,用于记录当前搜索游标的值。
typedef struct alist *List;//但链表指针类型
typedef struct alist{
int n,//表长
curr;//当前位置
int maxsize;//数组上界
ListItem *table;//存储表元素的数组
}Alist;
10.2 函数IterInit(L)
将表L的搜索游标初始化为表首元素的位置。
void IterInit(List L)
{
L->curr=0;
}
10.3 函数IterNext(L)
将表L的搜索游标从当前位置移向下一个位置。
void IterNext(List L)
{
L->curr=(L->curr+1)%L->n;
}
10.4 函数CurrItem(L)
返回表L的当前搜索游标处的元素。
ListItem *CurrItem(List L)
{
return &L->table[L->curr];
}
10.5 函数InsertCurr(x,L)
当前搜索游标处插入元素x。
void InsertCurr(ListItem x,List L)
{
ListInsert(L->curr,x,L);
}
10.6 函数DeleteCurr(L)
删除并返回表L的当前搜索游标处的元素。
ListItem DeleteCurr(List L)
{
ListItem x=ListDelete(L->curr+1,L);
L->curr=L->curr%L->n;
return x;
}
11 单循环链表的搜索游标
11.1 增加数据成员curr
在单循环链表结构中增加一个数据成员curr,用于记录当前搜索游标指针。
typedef struct clist *List;//单循环链表指针类型
typedef struct clist{
//单循环链表结构
int n;//表长
link last,//链表表尾指针
curr;//链表当前结点指针
}Clist;
11.2 函数IterInit()
将表L的搜索游标初始化为指向表首哨兵结点的指针。
void IterInit(List L)
{
L->curr=L->last->next;
}
11.3 函数IterNext(L)
将搜索游标指针下移。
void IterNext(List L)
{
L->curr=L->curr->next;
if(L->curr==L->last) L->curr=L->curr->next;
}
11.4 函数CurrItem(L)
返回游标处的元素
ListItem *CurrItem(List L)
{
if(L->n==0) return 0;
else return &L->curr->next->element;
}
11.5 函数InsertCurr(x,L)
由于curr指针已指向当前结点的前一结点,所以直接在curr指针处插入新结点即可,不用调用ListInsert(k,x,L)。
void InsertCurr(ListItem x,List L)
{
link y=NewNode();
y->element=x;
y->next=L->curr->next;
L->curr->next=y;
if(L->curr==L->last) L->last=y;
L->n++;
}
11.6 函数DeleteCurr(L)
由于curr指针已指向当前结点的前一结点,所以可直接修改指针删除当前结点,而不必调用ListDelete(k,L)。
ListItem DeleteCurr(List L)
{
link q=L->curr;
link p=q->next;
q->next=p->next;
if(p==L->last) {
L->last=q;L->curr=L->curr->next;}
ListItem x=p->element;
free(p);
L->n--;
return x;
}