零个或多个数据元素的有限序列
在较复杂的线性表中,一个数据元素可以由若干个数据项组成
ADT 线性表 (List)
Data
/*线性表的数据对象集合为{a1,a2,……,an},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一得到关系。*/
Operation
void initList(*L); //创建并初始化一个空线性表,如果成功返回true,修改表传指针
bool listEmpty(L); //判断一个线性表是否为空,不修改表传值
void clearList(*L); //清空一个线性表,成功返回true
bool getElem(L,i,*e); //从某个位置取出元素并赋值给e(i的范围是[1,L.length]),修改e的值所以传递一个指针,成功返回true
int locateElem(L,e); //查找线性表中是否有e,如果有返回它的位置(从1开始),否则返回0表示失败
bool listInsert(*L,i,e); //插入一个元素e在第i个元素之前(i的取值范围是[1,L.length+1]) ,成功返回true
bool listDelete(*L,i,*e); //删除在第i个位置上的元素(i的取值范围是[1,L.length]),删除的元素赋给e,成功返回true
int listLength(L); //返回线性表的元素个数
endADT
用一段地址连续的存储单元依次存储线性表的数据元素
描述顺序存储结构需要3个属性
存储空间的起始位置
线性表的最大存储容量
线性表的当前长度
任意时刻,线性表长度应该小于等于数组长度
取值
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
//用e返回L中第i个数据元素的值
Status GetElem(SqList L, int i, ElemType *e)
{
if(L.length==0 || i<1 || i>L.length)
return ERROR;
*e = L.data[i-1];
return OK;
}
插入
Status ListInsert(SqList *L, int i, ElemType e)
{
int k;
if(L->length == MAXSIZE) //表已满
return ERROR;
if(i<1 || i>L->length+1) //i不在范围内
return ERROR;
if(i <= L->length) //插入位置不在表尾
{
for(k = L->length-1; k >= i-1; k--)
L->data[k+1] = L->data[k];
}
L->data[i-1] = e; //插入新元素
L->length++;
return OK;
}
删除
Status ListDelete(SqList *L, int i, ElemType e)
{
int k;
if(L->length == 0) //表为空
return ERROR;
if(i<1 || i>L->length+1) //i不在范围内
return ERROR;
*e = L->data[i-1]; //取值
if(i < L->length) //删除位置不在表尾
{
for(k = i; k < L->length; k++)
L->data[k-1] = L->data[k];
}
L->length--;
return OK;
}
读取
Status GetElem(LinkList L,int i,ElemType *e)
{
int j;
LinkList p; //声明一工作节点
p = L->next; //让p指向L的第一个节点
j = 1; //计数器
while(p && j < i)
{
p = p->next;
j++;
}
if(!p || j>i)
return ERROR; //第i个元素不存在
*e = p->data; //获取数据
return OK;
}
插入
Status ListInsert(LinkList *L, int i, ElemType e) //一级也行,但统一用二级,万一呢
{
int j;
LinkList p,s;
p = *L;
j = 1;
while(p && j<i) //寻找第i个结点
{
p = p->next;
j++;
}
if(!p || j>i)
return ERROR;//第i个元素不存在
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
删除
Status ListDelete(LinkList *L, int i, ElemType *e) //一级也行,但统一用二级,万一呢
{
int j;
LinkList p, q;
p = *L;
j = 1;
while(p->next && j < i) //删除第i个位置的结点必须知道i-1结点的位置
{
p = p->next;
j++;
}
if(!(p->next) || j > i)
return ERROR; //第i个元素不存在
q = p->next; //q指向要删除结点
p->next = q->next;
*e = q->next;
free(q); //回收结点,释放内存
return OK;
}
整表创建
头插法
int CreateListHead(LinkList *L, int n) //二级指针,要在函数内改动表头指针的值
{
LinkList p;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node)); //头结点
(*L)->next = NULL;
for(i = 0; i<n; i++)
{
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100+1;
p->next = (*L)->next;
(*L)->next = p; //插入到表头
}
}
尾插法
int CreateListHead(LinkList *L, int n) //二级指针,要在函数内改动表头指针的值
{
LinkList p,r;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node)); //头结点
r = *L; //尾指针
for(i = 0; i<n; i++)
{
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100+1;
r->next = p;
r = p; //插入到表头
}
r->next = NULL;
}
整表删除
Status ClearList(LinkList *L) //一级也行,但统一用二级,万一呢
{
LinkList p, q;
p = (*L)->next; //指向第一个结点
while(p) //没到表尾
{
q = p->next; //获取下一个结点
free(p);
p = q;
}
(*L)->next = NULL; //头结点指针域置空
return OK;
}
/*线性表的静态链表存储结构*/
/*不提供struct的程序设计语言,可以使用一对并行数组data和cur来处理*/
#define MAXSIZE 1000 /*假设链表的最大长度是1000*/
typedef struct
{
ElemType data;
int cur; /*游标(Cursor),为0时表示无指向*/
}Component,StaticLinkList[MAXSIZE];
/*初始化*/
/*space[0].cur为头指针,“0”表示空指针*/
Status InitList(StaticLinkList space)
{
int i;
for(i = 0; i < MAXSIZE-1; i++) /*最后一个元素的cur单独处理*/
space[i].cur = i+1;
space[MAXSIZE-1].cur = 0; /*静态链表为空,最后一个元素的cur为0*/
return OK;
}
需要解决的问题:模拟动态链表的空间分配
自己实现malloc()
和free()
功能
/*若备用链表非空,则返回分配的结点下标,否则返回0*/
int Malloc_SLL(StaticLinkList space)
{
int i = space[0].cur; /*第一个备用空间的下标*/
if(space[0].cur)
space[0].cur = space[i].cur;/*把下一个分量作为备用*/
return i;
}
/*将下标为k的空闲结点回收到备用链表*/
Status Free_SLL(StaticLinkList space, int k)
{
space[k].cur = space[0].cur;/*将k结点插到备用链表的头*/
space[0].cur = k; /*更新备用链表的第一个下标*/
return OK;
}
/*在L中第i个元素之前插入新的数据元素e*/
Status ListInsert(StaticLinkList L, int i, ElemType e)
{
int j, k, l;
k = MAXSIZE - 1; /*最后一个元素下标*/
if(i < 1 || i > ListLength(L) + 1)
return ERROR;
j = Malloc_SLL(L); /*获得空闲分量的下标*/
if(j)
{
L[j].Data = e; /*赋值*/
for(l = 1; l <= i-1; l++) /*找到第i个元素之前的位置*/
k = L[k].cur;
L[j].cur = L[k].cur; /*把第i个元素之前的cur赋给当前元素的cur*/
L[k].cur = j; /*把新元素的下标赋值给前一个元素的cur*/
return OK;
}
return ERROR;
}
/*删除在L中第i个数据元素e*/
Status ListDelete(StaticLinkList L, int i)
{
int j, k;
if(i < 1 || i > ListLength(L))
return ERROR;
k = MAXSIZE - 1;
for(j = 1; j <= i-1; j++)
k = L[k].cur;
j = L[k].cur;
L[k].cur = L[j].cur;
free_SLL(L, j);
return OK;
}
/*返回L中的数据元素个数*/
int ListLength(StaticLinkList L)
{
int j = 0;
int i = L[MAXSIZE-1].cur;
while(i) /*最后一个有数据元素的cur为0*/
{
i = L[i].cur;
j++;
}
return j;
}
单链表终端结点的指针端由空指针改为指向头结点,形成一个环,变成单循环链表
循环判断条件:p->next != head
用指向终端结点的尾指针来表示循环链表,查找开头和终端结点就都方便
/*合并两个循环链表用尾指针极其方便*/
p = rearA->next;
rearA->next = rearB->next->next;
free(rearB->next); /*释放B链的头结点*/
rearB->next = p;
typedef struct DulNode
{
ElemType data;
struct DuLNode *prior; /*前驱*/
struct DuLNode *next; /*后继*/
}DulNode,*DuLinkList;