单链表的读取
Status GetElem(LinkList L,int i,ElemType *e)
{
int j;
LinkList p; /* 声明一结点p */
p = L->next; /* 让p指向链表L的第一个结点 */
j = 1; /* j为计数器 */
while (p && j<i) /* p不为空或者计数器j还没有等于i时,循环继续 */
{
p = p->next; /* 让p指向下一个结点 */
++j;
}
if ( !p || j>i )
return ERROR; /* 第i个元素不存在 */
*e = p->data; /* 取第i个元素的数据 */
return OK;
}
从1开始遍历,找第n个需要遍历n-1次,时间复杂度为O(n)
单链表的插入与删除
单链表的插入
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)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = p->next; /* 将p的后继结点赋值给s的后继 */
p->next = s; /* 将s赋值给p的后继 */
return OK;
}
(1)声明一指针p指向链表头结点,初始化j从1开始;
(2)当j<i时,就遍历链表,让p的指针像后移动,不断指向下一结点,j累加1;
(3)若到链表末尾p为空,则说明第i个结点不存在;
(4)否则查找成功,在系统中生成一个空节点s;
(5)将数据元素e赋值给s->data;
(6)单链表的插入标准语句s->next=p->next; p->next=s;
(7)返回成功。
单链表的删除
Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j;
LinkList p,q;
p = *L;
j = 1;
while (p->next && j < i) /* 遍历寻找第i个元素 */
{
p = p->next;
++j;
}
if (!(p->next) || j > i)
return ERROR; /* 第i个元素不存在 */
q = p->next;
p->next = q->next; /* 将q的后继赋值给p的后继 */
*e = q->data; /* 将q结点中的数据给e */
free(q); /* 让系统回收此结点,释放内存 */
return OK;
}
实际就是把p的后继结点改成p的后继结点的后继结点。
对于插入或删除数据越频繁的操作,单链表的效率优势就越明显。
单链表的整表创建
对于链表来说,所占用的空间大小和位置不需要预先分配划定,可根据系统的情况和实际的需求实时生成。
单链表整表创建的算法思路:
(1)声明一指针p和计数器变量i。
(2)初始化一空链表L。
(3)让L的头结点的指针指向NULL,即建立一个带头结点的单链表。
(4)循环:
1.生成一新节点赋值给p;
2.随机生成一数字赋值给p的数据域p->data;
3.将p插入到头结点与前一新节点之间。
此为头插法,即始终让新节点在第一的位置(插队)。
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
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个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0)); /* 初始化随机数种子 */
*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
r=*L; /* r为指向尾部的结点 */
for (i=0; i<n; i++)
{
p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */
p->data = rand()%100+1; /* 随机生成100以内的数字 */
r->next=p; /* 将表尾终端结点的指针指向新结点 */
r = p; /* 将当前的新结点定义为表尾终端结点 */
}
r->next = NULL; /* 表示当前链表结束 */
}
L指整个单链表,而r是指向尾结点的变量,r会随着循环不断地变化结点,L会随着循环增长为一个多结点的链表。
单链表的整表删除
单链表删除的算法思路:
(1)声明一指针p和q。
(2)将第一个结点赋值给p。
(3)循环:
1.将下一个节点赋值给q;
2.释放p;
3.将q赋值给p
单链表结构与顺序存储结构的优缺点
若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。
如游戏开发中,对于用户注册的个人信息,除了注册时插入数据外,接大多数情况都是读取,所以应该考虑用顺序存储结构。而游戏中的玩家的武器或者装备列表,随着玩家的游戏过程中,可能会随时增加或删除,此时要用单链表结构。
当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构。(因不需要考虑存储空间的大小问题)