欢迎访问我的新博客:http://www.milkcu.com/blog/
基本概念
链式存储结构,不要求逻辑上相邻的元素在物理上也相邻,没有顺序存储结构所具有的的弱点(在作插入或删除操作是,需移动大量元素),但同时也失去了顺序表可随机存取的优点。
单链表的结点由数据域和指针域构成,多个结点链结成一个链表。
代码实现
# include <stdio.h> # include <stdlib.h> # define OK 1 # define ERROR -1 # define OVERFLOW -2 typedef int ElemType; typedef int Status; typedef struct Lnode { ElemType data; struct Lnode * next; } Lnode, * LinkList; Status GetElem_L(LinkList L, int i, ElemType & e); Status ListInsert_L(LinkList & L, int i, ElemType e); Status ListDelete_L(LinkList & L, int i, ElemType & e); void CreateList_L(LinkList & L, int n); void MergeList_L(LinkList & La, LinkList & Lb, LinkList & Lc); Status ShowList_L(LinkList L); int main(void) { //测试函数 LinkList L; CreateList_L(L, 8); ShowList_L(L); //print create ListInsert_L(L, 3, 3); ShowList_L(L); //print insert ElemType e; ListDelete_L(L, 4, e); printf("delete e = %d\n", e); //print e delete ShowList_L(L); //print delete GetElem_L(L, 5, e); printf("getelem e = %d\n", e); //print getelem LinkList Lb; CreateList_L(Lb, 5); ShowList_L(Lb); //print Lb LinkList Lc; MergeList_L(L, Lb, Lc); ShowList_L(Lc); //print Lc return 0; } Status GetElem_L(LinkList L, int i, ElemType & e) { //算法2.8 //L为带头结点的单链表的头指针 Lnode * p = L->next; int j = 1; for(j = 1; p && j < i; j++) { p = p->next; } if(!p) { return ERROR; } e = p->data; return OK; } Status ListInsert_L(LinkList & L, int i, ElemType e) { //算法2.9 //在带头结点的单链线性表L中第i个位置之前插入元素e Lnode * p = L->next; int j = 1; Lnode * s = (Lnode * ) malloc(sizeof(Lnode)); s->data = e; for(j =1; p && j < i - 1; j++) { p = p->next; } if(!p) { return ERROR; } s->next = p->next; p->next = s; return OK; } Status ListDelete_L(LinkList & L, int i, ElemType & e) { //算法2.10 Lnode * p = L->next; int j = 1; for(j = 1; p && j < i - 1; j++) { p = p->next; } if(!p) { return ERROR; } Lnode * q; q = p->next; e = q->data; p->next = q->next; free(q); return OK; } void CreateList_L(LinkList & L, int n) { //算法2.11 //逆位序输入n个元素的值,建立带表头结点的单链线性表L L = (LinkList) malloc(sizeof(Lnode)); L->next = NULL; for(int i = 0; i < n; i++) { Lnode * p = (Lnode * ) malloc(sizeof(Lnode)); scanf("%d", & p->data); p->next = L->next; L->next = p; } } void MergeList_L(LinkList & La, LinkList & Lb, LinkList & Lc) { //算法2.12 //归并非递减排列的La和Lb到Lc //用La的头结点作为Lc的头结点 //本算法具有副作用 Lnode * pa = La->next; Lnode * pb = Lb->next; Lc = La; Lnode * pc = Lc; while(pa && pb) { if(pa->data <= pb->data) { pc->next = pa; pc = pc->next; pa = pa->next; } else { pc->next = pb; pc = pc->next; pb = pb->next; } } pc = (pa ? pa : pb); free(Lb); } Status ShowList_L(LinkList L) { Lnode * p = L->next; if(!p) { return ERROR; } while(p) { printf("%d ", p->data); p = p->next; } putchar('\n'); return OK; }
算法分析
GetElem_L()、ListInsert_L()、ListDelete_L()的时间复杂度均为O(n)。
对于算法2.9的ListInsert_L()也可通过下面改进算法实现,该算法对于在单链表中指定结点前插入结点比较实用(时间复杂度为O(1)),代码如下:
Status ListInsert_L(LinkList & L, int i, ElemType e) { //算法2.9改进版 //在带头结点的单链线性表L中第i个位置之前插入元素e Lnode * p = L->next; int j = 1; Lnode * s = (Lnode * ) malloc(sizeof(Lnode)); for(j = 1; p && j < i; j++) { p = p->next; } if(!p) { return ERROR; } //最终目的是在p前插入s s->next = p->next; p->next = s; s->data = p->data; p->data = e; }
对于算法2.10的ListDelete_L()也可通过下面函数实现,该算法对在于单链表中删除指定结点比较实用(时间复杂度为O(1)),代码如下:
Status ListDelete_L(LinkList & L, int i, ElemType & e) { //算法2.10改进版 //在带头结点的单链线性表L中,删除第i个元素,并由e返回其值 //未引入临时变量 Lnode * p = L->next; int j = 1; for(j = 1; p && j < i; j++) { p = p->next; } if(!p) { return ERROR; } e = p->data; p->data = p->next->data; p->next = p->next->next; free(& p->next); return OK; }
其实上面两个改进算法,在填空选择中经常用到。
参考资料
- 《数据结构:C语言版》(严蔚敏,吴伟民编著)
(全文完)