链式存储结构
单链表的定义
-
数据域:存储数据元素信息的域称为数据域。
-
指针域:存储直接后继位置的域称为指针域。
-
链:指针域中存储的信息称为指针或链。
-
结点:数据域和指针域的信息组成数据元素ai的存储映像,即为结点。
-
链式存储结构:n个结点链结成一个链表,即为线性表的链式存储结构。
-
单链表:链表中每个结点只包含一个指针域的链表称为单链表。
-
头指针:链表中第一个结点的存储位置叫做头指针。
-
链表中最后一个结点指针为“空”(NULL或“^”)。
-
头结点:链表第一个结点前附设的一个结点,且数据域可以不存储任何信息。(方便操作统一,也可没有)
-
单链表的存储结构:
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Node *LinkList; //定义LinkList
单链表的读取
- GetElem
Int GetElem(LinkList L,int i,ElemType *e)
{
LinkList p; //声明一个指针
p=L->next; //让p指向链表的第一个结点
int j=1;
while(p && j<i) //p不为空且计数器未等于i时
{
p=p->next; //重复使p指向下一个结点,直到p为空或找到第i个结点
++j;
}
if(!p || j>i) //若p为空或计数器大于i
return 0; //第i个结点不存在
*e=p->date; //获取第i个结点的数据
return 1;
}
- 时间复杂度为O(n)。
单链表的插入与删除
- 插入ListInsert:只需改变插入结点的指针域和插入位置前的结点的指针域即可。
Int ListInsert(LinkList *L,int i,ElemType e)
{
LinkList p; //定义插入位置的前一个的结点
LinkList s; //定义需要插入的结点
p = *L;
int j = 1;
while(p && j < i) //寻找第i-1个结点
{
p = p->next;
++j;
}
if(!p || j>i) //若p为空或计数器大于i
return 0; //第i个结点不存在
s = (LinkList)malloc(sizeof(Node)); //生成新结点
s->data = e;
s->next = p->next; //将p的后继结点赋值给s的后继
p->next = s; //将s赋值给p的后继
return 1;
- 删除ListDelete:原理同插入。
Int ListDelete(LinkList *L,int i,ElemType e)
{
LinkList p; //定义删除位置的前一个的结点
LinkList q; //定义需要删除的结点
p = *L;
int j = 1;
while(p && j < i) //寻找第i-1个结点
{
p = p->next;
++j;
}
if(!p || j>i) //若p为空或计数器大于i
return 0; //第i个结点不存在
q = p->next; //定位q
p->next = q->next; //将q的后继赋值给p的后继
*e = q->data; //将q的数据赋值给e
free(q); //删除此结点,释放内存
return 1;
}
- 时间复杂度为O(n),但在同一位置插入大量结点时,确定位置的时间复杂度为O(n),不断的插入和删除都为O(1)。
- 结论:对于插入或者删除数据越频繁的操作,单链表的效率优势越明显。
单链表的整表创建和删除
- 头插法创建
void CreateListHead(LinkList *L, int n)
{
LinkList p;
srand (time (0)); //初始化随机数种子
*L= (LinkList) malloc (sizeof (Node));
(*L)->next = NULL; //建立一个带头节点的单链表
for(int i = 0; i < n; i++)
{
p = (LinkList) malloc (sizeof (Node)); //生成新结点
p->date = rand () %100 + 1; //随机生成100以内的数字
p->next = (*L)->next;
(*L)->next = p; //插入到表头
}
}
- 尾插法创建
void CreateListHead(LinkList *L, int n)
{
LinkList p, r;
srand (time (0)); //初始化随机数种子
*L= (LinkList) malloc (sizeof (Node)); //建立整个线性表
r = *L; //r指向尾部的结点
for(int i = 0; i < n; i++)
{
p = (Node *) malloc (sizeof (Node)); //生成新结点
p->date = rand () %100 + 1; //随机生成100以内的数字
r->next = p; //将末端结点指向新结点
r = p; //将新节点定义为末端结点
}
r->next = NULL; //末端结点无指针域,当前链表结束
}
- 整表删除
Int ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next; //p指向第一个结点
while(p)
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL; //头节点指针域为空
return 1;
}
单链表结构&顺序存储结构
- 若线性表需频繁查找,很少进行插入删除时,选用顺序存储结构。如游戏开发中的用户个人信息。
- 若线性表需要频繁的插入和删除时,选用单链表结构。如游戏玩家的背包列表。
- 若事先知道线性表的大致长度,则选用顺序存储结构。
- 若线性表中元素个数变化较大,则选用单链表结构。