线性表
线性表的链式存储结构
- 定义:
我们把链表中第一个结点的存储位置叫做头指针,单链表的第一个结点前附设一个结点,叫头结点(如图):
- 头指针与头结点的异同
- 单链表的读取
获得链表第 i 个数据的算法思路:- 声明一个结点p指向第一个结点,初始化 j 从1开始;
- 当 j 小于1 时,就遍历链表,让 p 的指针向后移动,不断指向下一结点,j 累加1;
- 若到链表末尾p 为空,则说明第 i 个元素不存在;
- 否则查找成功,返回结点 p 的数据;
代码如下:
/*初始条件 : 顺序链表L 已经存在,1 <= i <= ListLength(L)*/
/*操作结果 :用e 返回L 中第 i 个数据元素的值 */
Staus GetElm (LinkList L, int i, ElemType *e){
int j;
LinkList p; //声明一结点p
p = L->next; //让p指向链表L的第一个结点
j = 1; // j 为计数器
while(p && j < 1){ //p 不为空或计数器还没有等于 i 时,循环继续
p = p->next; //指向下一个结点
++j;
}
if ( !p || j > i)
return ERROR; //第 i 个元素不存在
*e = p ->data; //取第 i 个元素的数据
return OK;
}
- 单链表的插入与删除
单链表第 i 个数据插入结点的算法思路- 声明一结点p 指向链表第一个结点,初始化j 从1 开始;
- 当 j 小于 1 时,就遍历链表,让p的指针向后移动,不断指向下一结点,j 累加 1;
- 若到链表末尾p 为空,则说明第 i 个元素不存在;
- 否则查找成功,在系统中生成一个空结点 s;
- 将数据元素e 赋值给 s -> data;
- 单链表的插入标准语句 s->next = p->next;p->next = s;
- 返回成功
实现代码算法如下:
/*初始条件 : 顺序链表L 已经存在,1 <= i <= ListLength(L)*/
/*操作结果 :在L 中第 i 个位置之前插入新的数据元素e ,L的长度加 1 */
Staus GetElm (LinkList *L, int i, ElemType e){
int j;
LinkList p, s;
j = 1;
while (p && j < i){ //寻找第 i 个结点
p = p -> next;
++j;
}
if( !p || j >i)
return ERROR; //第 i 个元素不存在
s = (LinkLinst)malloc(sizeof(Node)); //生成新结点;
s -> data = e;
s -> next = p ->next; //将 p的后继结点赋值给 s 的后继
p -> next = s; //将 s 赋值给 p 的后继
return OK;
}
单链表第 i 个数据删除结点的算法思路
- 声明一结点p 指向链表第一个结点,初始化j 从1 开始;
- 当 j 小于 1 时,就遍历链表,让p的指针向后移动,不断指向下一结点,j 累加 1;
- 若到链表末尾p 为空,则说明第 i 个元素不存在;
- 否则查找成功,将欲删除的结点 p -> next 赋值给 q ;
- 单链表的删除标准语句 p -> next = q ->next;
- 将q结点中的数据赋值给 e ,并作为返回;
- 释放q结点;
- 返回成功
实现代码算法如下:
/*初始条件 : 顺序链表L 已经存在,1 <= i <= ListLength(L)*/
/*操作结果 :在L 中第 i 个位置之前插入新的数据元素e ,L的长度加 1 */
Staus GetElm (LinkList *L, int i, ElemType *e){
int j;
LinkList p, s;
j = 1;
while (p && j < i){ //寻找第 i 个结点
p = p -> next;
++j;
}
if( !p || j >i)
return ERROR; //第 i 个元素不存在
q = p -> next;
p -> next = q -> next; //将q 的后继赋值给 p 的后继
*e = q -> next; //将q 的数据结点给数据 *e;
free(q); //释放内存
return OK;
}
-
单链表的整表创建
单链表的整表创建的算法思路- 声明一结点 p 和 计数器变量 j;
- 初始化一空链表L
- 让L的头结点的指针指向NULl,即建立一个带头结点的单链表;
- 循环:
- 生成一新结点赋值给p;
- 随机一串数字赋值给 p的数据域 p-> data;
- 将 p插入到头结点与之前一新结点之前
代码如下:
/* 随机产生n个元素的值,建立带头结点的单链表(头插法)*/ void 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; //随机产生100以内的数字 p ->next = (*L) ->next; (*L) ->next = p; //插到表头 }
}
`````` /* 随机产生n个元素的值,建立带头结点的单链表(尾插法)*/ void CreateListHead ( LinkList *L, int n){ LinkList p,r; int i; srand(time(0); //初始化随机种子 *L = (LinkList)malloc(sizeof(Node)); r = *L; //r为指向尾部的指针 for (i = 0; i < n; i++){ p =(LinkList)malloc(sizeof(Node)); //生成新结点 p -> data = rand()%100 + 1; //随机产生100以内的数字 r -> next = p; //将表尾终端结点的指针指向新结点 r = p; //将当前的新结点定义为表尾终端结点 } r -> next = NULLl; } ```
-
单链表的整表删除
单链表的整表删除算法思路:- 声明一结点 p 和 q;
- 将第一个结点赋值给 p;
- 循环:
- 将下一结点赋值给q;
- 释放p;
- 将q 赋值给 p;
代码如下
/* 初始条件 :顺序线性表L 已经存在,操作结果 :将 L充值为空表*/ Status ClearList(LinkList *L){ LinkList p, q; p = (* L) -> next; //p指向第一个结点 while (p){ //直到表尾 q = p -> next; free(p); p = q; } (*L) -> next = NULL; //头结点指针域为空 return OK; }
-
单链表结构与顺序储存结构优缺点
-
静态链表
线性表的静态链表存储结构代码如下:#define MAXSIZE 1000 //假设静态链表最大长度为1000 typedef struct{ ElemType data; int cur //游标(Cursor),为0时表示无指向; }Component,StaticLinkList[MAXSIZE];
静态链表的插入:
/*若备用空间链表非空,则返回分配的结点下标,否则返回0*/ int Malloc_SLL(StaticLinkList space){ int i = space[0].cur; if(space[0].cur){ space[0].cur = space[i].cur; return i; } }
插入代码:
/* 在 L 中第 i 个元素之前插入新的数据元素 e*/ Status LinstInsert(StatucLinkList L, int i, ElemType e){ int j, k, l; k = MAX_SIZE - 1; //注意 k 首先是最后一个元素的下标 if(i < 1 || i > ListLength(L) + 1) return ERROR; j = Malloc_SLL(L); //获得空间分量的下标 if(j){ L[j].data = e; //将数据赋值给此分量的data; for(l = i; l <= i - 1; l++) //找到第 i 个元素之前的位置; k = L[k].cur; L[j].cur = L[k].cur; //把第 i 个元素之前的cur赋值给新的cur; L[K].cur = j; //把新元素的下标赋值给第 i 个元素之前的cur return OK; } return ERROR; }
静态链表的删除
备用代码 :/*将下标为 k 的空闲结点回收到备用链表*/ void Free_SSL(StaticLinkList space, int k){ space[k].cur = space[0].cur; //把第一个元素cur 值赋给要删除的分量cur; space[0].cur = k; //把要删除的分量下标赋值给第一个元素的cur; }
删除代码如下:
/*删除在L 中第 i 个数据元素 e*/ Status LinstInsert(StatucLinkList L, int i){ int j, k; if(i < 1 || i > ListLength(L) + 1) return ERROR; k = MAX_SIZE - 1; for( j = 1; j <= i - 1; j++) k = L[k].cur; j = L[k].cur; L[K].cur = L[j].cur; Free_SSL(L,j); return OK; }
静态链表的优缺点:
-
循环链表
定义:将链表中终端结点的指针端有由空指针改为指向头结点的,就使整个单链表形成一个环。
不用头指针,而是用指向终端结点的尾指针表示循环链表,便于查找开始结点和终端结点
将两个循环链表合并成一个表(尾指针分别为:rearA,rearB):
代码如下:
p = rearA -> next; //保存A表的头结点 rearA -> next = rearB -> next ->next; //将本是指向B表的第一个结点赋值给rearA -> next; rearB ->next = p; //将A表的头结点赋值给rearB -> next; free(p); //释放p;
10.双向链表
-
定义:双向链表是在单链表的每个结点中,在设置一个指向其前驱结点的指针域 .
-
双向链表的储存结构:
typedef struct{ ElemType data; struct DuLNode *prior; //直接前驱指针; struct DuLNode *bext; //直接后继指针; }DulNode, *DuLinkList;
-
插入
-
删除
代码如下:p -> prior -> next = p -> next; //把p -> next 赋值给p -> prior 的后继 ; p -> next -> prior = p -> prior; //把p -> prior 赋值给 p -> next 的前驱; free(p); //释放结点