线性表的链式表示
- 链式存储结构可以进行较快地插入和删除,并且存储结构可以反映数据之间的逻辑关系
- 链式存储分为:单链表、双链表、循环单链表、循环双链表和静态链表。
一、单链表
-
单链表的结点结构
-
定义:
typedef struct LNode{ ElemType data; struct LNode *next; }LNode,*LinkList;
-
头结点
头结点的数据域可以不设值,也可以记录表长等信息,计算数据结点的个数时不需计算头结点。
引入头结点的好处
- 方便运算的实现
- 统一空表和非空表的实现
-
头插法创建单链表
LinkList List_HeadInsert(LinkList &L){ LNode *s,int x; L = (LinkList)malloc(sizeof(LNode)); L->next = NULL; scanf("%d",&x); while(x!=9999){ s = (LNode*)malloc(sizeof(LNode)); s->data = x; s->next = L->next; L-next = s; scanf("%d",&x); } return L; }
-
头插法创建单链表
LinkList List_TailInsert(LinkList &L){ LNode *s,int x; LNode *r = L; L = (LinkList)malloc(sizeof(LNode)); *r = L; scanf("%d",&x); while(x!=9999){ s = (LNode*)malloc(sizeof(LNode)); s->data = x; r->next = s; r = s; scanf("%d",&x); } r->next = NULL; return L; }
-
插入结点
-
复杂度为O(n)
p = GetElem(i-1); s->next = p->next; p->next = s->next;
-
复杂度为O(1)
s->next = p->next; p->next = s; ElemType temp = p->data; p->data = s->data; s->data = temp;
-
-
删除结点
-
复杂度为O(n)
p = GetElem(i-1); q = p->next; p->next = q->next; free(q)
-
复杂度为O(n)
q = p->next; p->data = q->data; p->next = q->next; free(q);
-
二、双链表
-
定义
typedef struct DNode{ ElemType data; struct DNode *prior,*next; }DNode,*DinkList;
插入和删除操作的时间复杂度为O(1)
-
插入操作
s->next = p->next;//把s结点插入到p结点后 p->next-prior = s; p->next = s; s-prior = p;
-
删除操作
p->next = q->next;//删除p结点的后继节点q q->next->prior = p; free(q);
三、循环单链表
- 循环单链表与单链表的区别在于:尾结点不指向NULL,而是指向首结点L,解r->next = L。
- 循环单链表在任一个位置的插入和删除都是等价的,无需判断是否是表尾。
- 可以从任一位置遍历整个链表。
- 当单链表的操作经常在表头和表尾进行,采用熏坏单链表不设头指针而设尾指针,效率会更高。例如,在最后一个元素插入一个元素和删除第一个元素。注意,当链表操作要删除最后一个元素时效率并不高,与链表的长度有关系。
四、循环双链表
当链表常用的操作是在末尾插入和删除结点时,应该选用带头结点的循环双链表,例如,删除第一个元素、删除最后一个元素、在第一个元素之前插入元素、在最后一个元素之后插入新元素。
带头结点的双循环链表L为空的条件是:
L->prior = L && L->next = L
五、静态链表
- 静态链表借助数组来描述线性表的链式存储结构,需要提前分配一块连续的地址空间。
- 静态链表以next==-1为结束标志
- 静态链表的插入、删除操作与动态链表相同,只需要修改指针,但静态链表不需要移动元素。
六、顺序表和链表的比较
-
存取(读写)方式
顺序表可以顺序、随机存取,链表只能顺序存取。
-
逻辑结构和物理结构
采用顺序结构时,逻辑上相邻的元素物理存储位置也相同;链表则不一定,对应的逻辑关系是通过指针链接表示的。
-
查找、插入和删除操作
对于按值查找,顺序表无序时,两者的时间复杂度均为O(n);顺序表有序时,可以采用折半查找。
顺序表的插入、删除操作,平均需要移动半个表长的元素,链表只需要修改指针域即可。
由于链表的每个结点都带有指针域,故而存储密度不大。
-
空间分配
顺序存储的静态存储分配和动态存储分配都具有一定的缺点;而链表只在需要的时候申请分配,只要内存有空间就可以分配,操作高效、灵活。