线性表链式存储结构
其特点是用一组任意的存储单元存储数据元素,这组存储单元可以存在内存中未被占用的任意位置。出来存储本身的信息外,还需要存储一个指示其直接后继的存储位置的信息。这连个位置分别叫做:数据域 和 指针域。这两部分信息组成数据元素称为结点(Node)。该种结构只包含一个指针域,因此叫做单链表。
单链表存储结构代码描述
typedef struct Node
{
ElemType data; //数据域
struct Node *Next; //指针域
}Node;
typedef struct Node* LinkList;
单链表中头结点和头指针的异同
单链表中,头结点和头指针有较大差别,理解它俩的区别对后续理解单链表的各项操作至关重要。
头指针是指链表指向的第一个结点指针,若链表有头结点,则是指向头结点的指针。头指针通常就是指针变量的名字(这一点很重要)。无论链表是否为空,头指针都不为空。
线性表链式存储结构下查找,插入,删除数据
读取数据(时间复杂度o(n))
在线性表的顺序存储结构中想查找一个元素的存储位置是很容易的,但在单链表中,由于我们并不能够知道第i个位置在哪,我们需要从第一个结点开始挨个查找。
//用e返回链表中第i个位置的值
GetElem(LinkList L,int i, ElemType *e)
{
int j;
LinkList p; //链表名表示的是头指针,指向头结点
p = L->next;
j = 1;
//直到p为空或者找到第i-1(也就是i个元素停止,此时p指向的就是第i个元素)
while( p && j<i)
{
p = p->next;
++j;
}
if (!p || j>i)
{
return ERROR;
}
*e = p->data;
retrun OK;
}
插入数据(时间复杂度o(n))
在单链表中,由于我们并不能够知道第i个位置在哪,我们需要从第一个结点开始挨个查找,再执行插入。
//在第i个位置之前插入新的数据元素e
ListInsert(LinkList *L, int i, ElemType e)
{
int j;
LinkList p, a;
p = *L;
j = 1;
//这两部分和上面查找是一样的
while( p && j<i)
{
p = p->next;
++j;
}
if (!p || j>i)
{
return ERROR;
}
s = (LinkList)malloc(sizeof(Node));
s->data = e;
//因为此时p指针指向的是当前位置,所以将p->next给被插入结点s的s->next,然后将s给p->next;
s->next = p->next;
p->next = s;
return Ok;
}
删除数据(时间复杂度o(n))
在单链表中,由于我们并不能够知道第i个位置在哪,我们需要从第一个结点开始挨个查找,再执行删除。
ListDelete(LinkList *L, int i, ElemTpye *e)
{
int j;
LinkList p, q;
p = *L;
j = 1;
//这两部分和上面查找是一样的
while( p && j<i)
{
p = p->next;
++j;
}
if (!p || j>i)
{
return ERROR;
}
q = p->next;
p->next = q->next;
//这里确实是删除q的,因为此时q正好空出来啦
*e = q->data;
free(q);
return OK;
}
线性表顺序和链式存储的效率PK
如果我们不知道第i个元素的指针位置,单链表数据结构在插入和删除操作上,是没有太大优势的。单如果我们希望从第i个位置开始,插入连续10个元素,这对于顺序存储结构意味着,没插入一次都要移动n-i个位置,所以每次都是o(n),而链表只是第一次找i位置时要o(n),而之后都是0(1)就行了。所以比如说一个游戏中人物的装备,这种情况下单链表的优势就比较明显了,因为这种情况需要频繁的插入。
单链表结构和顺序存储结构优缺点
存储分配方式
顺序存储结构用一段连续的存储单元依次存储线性表的数据元素。
单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素。
时间性能
查找
顺序o(1)
单链表o(n)
插入和删除
顺序存储结构需要平均移动表长一半的元素,时间复杂度为0(n).
单链表在计算出某位置的指针后,插入和删除时的时间复杂度为o(1).
空间性能
顺序存储结构需要预先分配内存空间,分大了浪费,分小了不够用。
单链表不需要分配内存空间 ,元素个数不受限制。