抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

链表

线性表的链式储存结构

有很多个结点,每个结点都有一个指针头,左后一个结点的指针为空,通常设置为null或者^表示,第一个结点称作为头结点

头指针 头结点
头指针是指链表指向第一个结点的指针,若链表有头结点,则指向头结点的指针 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可以存放链表的长度)
头指针具有标识作用,所以常用头指针冠以链表的名字 有了头结点,在对第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了
无论链表是否为空,头指针均不为空,头指针是链表的必要元素 头结点不一定是链表必须要素

单链表

空链表

链表结构

1
2
3
4
5
typedef struct Node{
ElemType data; //数据域
struct Node *Next; //指针域
}Node;
typedef struct Node *LinkList;

(1) 获取当前元素

初始条件:顺序线性表*L*存在,*1<=i<=ListLength(L)*,操作结果:用*e*返回*L*中第*i*个元素的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Status GetElem(LinkList L, int i, ElemType *e)
{
int j;
LinkList p;
p=L->next;
j=1;
while(p && j<i) //若p不是尾节点,则将p的节点变为下一个元素,直到j=i-1,此时指针变为第i元素的地址
{
p = p->next;
++j;
}
if(!p || j>i) //若p是尾节点,或者j越界
{
return ERROR;
}
*e = p->data; //获取第i地址的data数据
return OK;
}

从头开始找,直到第个元素为止由于这个算法的时间复杂度取决于1的位置,当i=1​时,则不需要遍历,而i=n​时则遍历n-1​次可以因此最坏情况的时间复杂度为O(n)​。

(2) 链表插入

需要先把需要插入的数据的头指向原来的后一位,然后再将原来的前一位指向插入的数据的内容,否则操作相反会导致后一位没有上级

1
2
3
//正确的插入方式
s->next=p->next;
p->next=s;

初始条件:顺序线性表*L*已存在,*1<=i<=ListLength(L)*操作结果:在*L*中第*i*个位置之前插入新的数据元素*e**L*的长度加*1*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}
//动态分配一个内存给新创建需要插入的s
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}

(3) 链表删除

初始条件:顺序线性表*L*已存在,*1<=i<=ListLength(L)*操作结果:删除在*L*中第*i*个位置的数据元素,并用*e*​返回值*L*的长度减*1*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Status ListDelete(LinkList *L, int i, ElemType *e)
{
int j;
LinkList p, q;
p = *L;
j = 1;
while(p->next && j<i)
{
p = p->next;
++j;
}
if(!(p->next) || j>i)
{
return ERROR;
}
q = p->next;
p->next = q->next;

*e = q->data;
free(q);
return OK;
}

时间复杂度为O(n),对于插入或删除数据越频繁的操作,单链表的效率优势就越是明显