顺序表在插入和删除操作需要移动大量元素。数组的大小不好确定,且存储分配需要一整段连续的存储空间,造成很多碎片。因此在需要经常插入和删除的线性表中,需要通过链式存储方式实现。线性表的链式表示称为链表。以下为我在学习和实战练习过程中所做的笔记,可供参考。
一、单链表 逻辑上相邻的两个元素在物理位置上不相邻。
单链表结点的定义:
1 2 3 4 5 typedef int ElemType;typedef struct LNode { ElemType data; struct LNode *next ; } LNode, *LinkList;
头指针:链表中第一个结点的存储位置,用来标识单链表。
头结点:在单链表第一个结点之前附加的个结点,为了操作上的方便。
若链表有头结点,则头指针永远指向头结点,不论链表是否为空,头指针均不为空,头指针是链表的必须元素,他标识一个链表。
头结点是为了操作的方便而设立的,其数据域一般为空,或者存放链表的长度。有头结点后,对在第一结点前插入和删除第一结点的操作就统一了,不需要频繁重置头指针。但头结点不是必须的。
优点:
插入和删除操作不需要移动元素,只需要修改指针。
不需要大量的连续存储空间。
缺点:
单链表附加指针域,也存在浪费存储空间的缺点。
查找操作时需要从表头开始遍历,依次查找,不能随机存取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int main () { LinkList L; LinkList search; CreatList2 (L); PrintList (L); search=GetElem (L,2 ); if (search!=NULL ) { printf ("按序号查找成功\n" ); printf ("%d\n" ,search->data); } search=LocateElem (L,6 ); if (search!=NULL ) { printf ("按值查找成功\n" ); printf ("%d\n" ,search->data); } ListFrontInsert (L,2 ,99 ); PrintList (L); ListDelete (L,4 ); PrintList (L); }
头插法新建链表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 LinkList CreatList1 (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; }
尾插法新建链表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 LinkList CreatList2 (LinkList &L) { int x; L = (LinkList)malloc (sizeof (LNode)); LNode *s, *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; }
按序号查找结点值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LNode *GetElem (LinkList L, int i) { int j = 1 ; LNode *p = L->next; if (i == 0 ) return L; if (i < 1 ) return NULL ; while (p && j < i) { p = p->next; j++; } return p; }
按值查找:
1 2 3 4 5 6 7 LNode *LocateElem (LinkList L, ElemType e) { LNode *p = L->next; while (p != NULL && p->data != e) p = p->next; return p; }
新结点插入第 i 个位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 bool ListFrontInsert (LinkList L, int i, ElemType e) { LinkList p = GetElem (L, i - 1 ); if (NULL == p) { return false ; } LinkList s = (LNode *)malloc (sizeof (LNode)); s->data = e; s->next = p->next; p->next = s; return true ; }
删除第 i 个结点:
1 2 3 4 5 6 7 8 9 10 11 12 13 bool ListDelete (LinkList L, int i) { LinkList p = GetElem (L, i - 1 ); if (NULL == p) { return false ; } LinkList q; q = p->next; p->next = q->next; free (q); return true ; }
打印链表中每个结点的值:
1 2 3 4 5 6 7 8 9 10 void PrintList (LinkList L) { L = L->next; while (L != NULL ) { printf ("%3d" , L->data); L = L->next; } printf ("\n" ); }
二、双链表 双链表结点定义:
1 2 3 4 5 typedef int ElemType;typedef struct DNode { ElemType data; struct DNode *prior,*next; }DNode,*DLinkList;
双链表增删查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int main () { DLinkList DL; DLinkList search; Dlist_head_insert (DL); PrintDList (DL); search=GetElem (DL,2 ); if (search!=NULL ) { printf ("按序号查找成功\n" ); printf ("%d\n" ,search->data); } DListFrontInsert (DL,3 ,99 ); PrintDList (DL); DListDelete (DL,2 ); PrintDList (DL); system ("pause" ); }
双向链表头插法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 DLinkList Dlist_head_insert (DLinkList &DL) { DNode *s;int x; DL=(DLinkList)malloc (sizeof (DNode)); DL->next=NULL ; DL->prior=NULL ; scanf ("%d" ,&x); while (x!=9999 ){ s=(DLinkList)malloc (sizeof (DNode)); s->data=x; s->next=DL->next; if (DL->next!=NULL ) { DL->next->prior=s; } s->prior=DL; DL->next=s; scanf ("%d" ,&x); } return DL; }
双向链表尾插法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 DLinkList Dlist_tail_insert (DLinkList &DL) { int x; DL=(DLinkList)malloc (sizeof (DNode)); DNode *s,*r=DL; DL->prior=NULL ; scanf ("%d" ,&x); while (x!=9999 ){ s=(DNode*)malloc (sizeof (DNode)); s->data=x; r->next=s; s->prior=r; r=s; scanf ("%d" ,&x); } r->next=NULL ; return DL; }
按序号查找结点值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 DNode *GetElem (DLinkList DL,int i) { int j=1 ; DNode *p=DL->next; if (i==0 ) return DL; if (i<1 ) return NULL ; while (p&&j<i) { p=p->next; j++; } return p; }
新结点插入第 i 个位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bool DListFrontInsert (DLinkList DL,int i,ElemType e) { DLinkList p=GetElem (DL,i-1 ); if (NULL ==p) { return false ; } DLinkList s=(DLinkList)malloc (sizeof (DNode)); s->data=e; s->next=p->next; p->next->prior=s; s->prior=p; p->next=s; return true ; }
删除第 i 个结点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bool DListDelete (DLinkList DL,int i) { DLinkList p=GetElem (DL,i-1 ); if (NULL ==p) { return false ; } DLinkList q; q=p->next; if (q==NULL ) return false ; p->next=q->next; if (q->next!=NULL ) { q->next->prior=p; } free (q); return true ; }
链表打印:
1 2 3 4 5 6 7 8 9 10 void PrintDList (DLinkList DL) { DL=DL->next; while (DL!=NULL ) { printf ("%3d" ,DL->data); DL=DL->next; } printf ("\n" ); }
三、循环链表和静态链表 循环单链表与单链表的区别在于,表中最后一个结点的 next 指针不是 NULL,而是指向头结点 L,从而整个链表形成一个环。x
循环双链表与双链表的区别在于,表中最后一个结点的 next 指针不是 NULL,而是指向头结点 L,同时 L->prior
指向尾节点。当循环双链表为空时,其头结点的 prior 域和 next 域都等于 L。
静态链表是借助数组来描述线性表的链式存储结构:
1 2 3 4 5 #define Maxsize 50 typedef struct { ElemType data; int next; }SLinkList[Maxsize];