链表
线性表的链式储存结构
有很多个结点,每个结点都有一个指针头,左后一个结点的指针为空,通常设置为null或者^表示,第一个结点称作为头结点
头指针 | 头结点 |
---|---|
头指针是指链表指向第一个结点的指针,若链表有头结点,则指向头结点的指针 | 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可以存放链表的长度) |
头指针具有标识作用,所以常用头指针冠以链表的名字 | 有了头结点,在对第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了 |
无论链表是否为空,头指针均不为空,头指针是链表的必要元素 | 头结点不一定是链表必须要素 |
单链表
空链表
链表结构
1 | typedef struct Node{ |
(1) 获取当前元素
初始条件:顺序线性表*L*
存在,*1<=i<=ListLength(L)*
,操作结果:用*e*
返回*L*
中第*i*
个元素的值
1 | Status GetElem(LinkList L, int i, ElemType *e) |
从头开始找,直到第个元素为止由于这个算法的时间复杂度取决于1的位置,当
i=1
时,则不需要遍历,而i=n
时则遍历n-1
次可以因此最坏情况的时间复杂度为O(n)
。
(2) 链表插入
需要先把需要插入的数据的头指向原来的后一位,然后再将原来的前一位指向插入的数据的内容,否则操作相反会导致后一位没有上级
1 | //正确的插入方式 |
初始条件:顺序线性表*L*
已存在,*1<=i<=ListLength(L)*
操作结果:在*L*
中第*i*
个位置之前插入新的数据元素*e*
,*L*
的长度加*1*
1 | Status ListInsert(LinkList *L, int i, ElemType e) |
(3) 链表删除
初始条件:顺序线性表*L*
已存在,*1<=i<=ListLength(L)*
操作结果:删除在*L*
中第*i*
个位置的数据元素,并用*e*
返回值,*L*
的长度减*1*
1 | Status ListDelete(LinkList *L, int i, ElemType *e) |
时间复杂度为
O(n)
,对于插入或删除数据越频繁的操作,单链表的效率优势就越是明显