数据结构与算法 -- 链表
生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法 -- 链表
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、鏈表介紹
1、鏈表有地址不連續的結點序列,必須通過指針相互連接。 2、鏈表的分類: (1)單向線性鏈表 每個節點中除了存儲數據結構內容以外,還需要保存指向下一個節點的指針,叫做后指針。最后一個節點的后指針為NULL。其中第一個節點叫做頭節點,指向頭結點的指針叫做頭指針。最后一個節點叫做尾節點。 (2)單向循環鏈表 與單向線性鏈表不同的是,鏈表尾節點的后指針指向頭節點。首尾相接構成環狀結構。 (3)雙向線性鏈表 每個節點除了存放本身元素以外,還需要保存指向下一個節點的指針,叫做后指針,以及指向前一個節點的指針,叫做前指針。雙向線性鏈表中頭節點的前指針為空,以及尾節點后指針也是空指針。 (4)雙向循環鏈表 與雙向線性鏈表相比,頭節點中的前指針指向尾節點,尾節點的后指針指向頭節點,構成環狀結構。 (5)其他 除了以上鏈表以外,還有很多鏈表,如數組鏈表、鏈表數組等等。 簡單介紹完了,下面開始一一講解下。二、單向線性鏈表 (重點)
1、參看:C中鏈表
鏈表中最簡單的一種是單向鏈表,它包含兩個域,一個信息域和一個指針域。這個鏈表指向列表中的下一個節點,而最后一個節點則指向一個空值。如下:一個單向鏈表的節點被分成兩個部分。第一部分保存或者顯示關于節點的信息,第二部分存儲下一個節點的地址。單向鏈表只可向一個方向遍歷。 鏈表最基本的結構是在每個節點保存數據和到下一個節點的地址,在最后一個節點保存一個特殊的結束標記,另外在一個固定的位置保存指向第一個節點的指針,有的時候也會同時存儲指向最后一個節點的指針。一般查找一個節點的時候需要從第一個節點開始每次訪問下一個節點,一直訪問到需要的位置。但是也可以提前把一個節點的位置另外保存起來,然后直接訪問。當然如果只是訪問數據就沒有必要了,不如在鏈表上存儲指向實際數據的指針。這樣一般是為了訪問鏈表中的下一個或者前一個(需要存儲反向的指針,見下面的雙向鏈表)節點。 相對于下面的雙向鏈表,這種普通的,每個節點只有一個指針的鏈表也叫單向鏈表,或者單鏈表。通常用在每次只會按順序遍歷這個鏈表的時候(例如圖的鄰接表,通常都是按固定順序訪問的)。 舉個例子:建立鏈表保存學生的信息,并且可以進行,插入、刪除操作,并將學生的信息輸出。 參看:鏈表實現學生信息管理 #include <stdio.h> #include <string.h> #include <stdlib.h> struct node {int number;char name[10];char sex[5];int classes;int tel;struct node *next; }; typedef struct node NODE; void scanning(NODE *head); /*申明瀏覽函數*/ NODE *insert(NODE *head); /*申明插入函數*/ void del(NODE *head); /*申明刪除函數*/ void fix(NODE *head); /*申明修改函數*/int main (void) {int choice;int flag=1;NODE *head;head=(NODE *)malloc(sizeof(NODE));head->next=NULL;while(flag){printf("請選擇功能:\n 1—信息瀏覽\n 2—插入信息\n 3—刪除信息\n 4-修改信息\n 0—退出程序\n");scanf("%d",&choice);switch(choice){case 1:scanning(head);break;case 2:head=insert(head);break;case 3:del(head);break;case 4:fix(head);break;case 0:flag=0; break;}}printf("\n");printf("謝謝使用!\n");return 0; }void scanning(NODE *head) /*定義瀏覽函數*/ {NODE *t;t=head;t=t->next;if(t==NULL){printf("\n");printf("無記錄!請先輸入學生信息:\n");printf("\n");}while(t!=NULL){printf("學號:%d\n",t->number);printf("姓名:%s\n",t->name);printf("性別:%s\n",t->sex);printf("班級:%d\n",t->classes);printf("聯系方式:%d\n",t->tel);printf("\n");t=t->next;} }NODE *insert(NODE *head) /*定義插入函數*/ {NODE *t,*p;int a,b,c;char e[10],f[5];t=(NODE *)malloc(sizeof(NODE));p=head;if(p->next==NULL) /*原本無元素*/{printf("請輸入學號:\n");scanf("%d",&a);printf("請輸入姓名:\n");scanf("%s",e);printf("請輸入性別:\n");scanf("%s",f);printf("請輸入班級:\n");scanf("%d",&b);printf("請輸入號碼:\n");scanf("%d",&c);t->number=a;strcpy(t->name,e);strcpy(t->sex,f);t->classes=b;t->tel=c;p->next=t;t->next=NULL;}else /*原本有元素*/{printf("請輸入學號:\n");scanf("%d",&a);printf("請輸入姓名:\n");scanf("%s",e);printf("請輸入性別:\n");scanf("%s",f);printf("請輸入班級:\n");scanf("%d",&b);printf("請輸入號碼:\n");scanf("%d",&c);t->number=a;strcpy(t->name,e);strcpy(t->sex,f);t->classes=b;t->tel=c;t->next=p->next;p->next=t;}return head; }void del(NODE *head) /*定義刪除函數*/ {int m;NODE *s,*t;s=head;t=head->next;printf("請輸入你要刪除的同學的學號:\n");scanf("%d",&m);while(t!=NULL){if((t->number)!=m){s=s->next;t=t->next;}else{s->next=t->next;free(t);break;}} }void fix(NODE *head) /*定義修改函數*/ {NODE *p,*t;int m,q,z=1;p=head;t=(NODE *)malloc(sizeof(NODE));printf("請輸入你要修改的學生的學號:\n");scanf("%d",&m);p=p->next;while(p!=NULL){if((p->number)==m){while(z){printf("你想要修改哪項數據?\n 1代表學號\n 2代表姓名\n 3代表性別\n 4代表班級\n 5代表聯系方式\n (注意:修改完畢請輸入0)\n");scanf("%d",&q);switch(q){case 1:printf("請輸入修改后的學號!\n");scanf("%d",&t->number);p->number=t->number;break;case 2:printf("請輸入修改后的姓名!\n");scanf("%s",t->name);strcpy(p->name,t->name);break;case 3:printf("請輸入修改的性別!\n");scanf("%s",t->sex);strcpy(p->sex,t->sex);break;case 4: printf("請輸入修改的班級!\n");scanf("%d",&t->classes);p->classes=t->classes;break;case 5:printf("請輸入修改后的聯系方式!\n");scanf("%d",&t->tel);p->tel=t->tel;break;case 0:z=0;break;}}}p=p->next;} }
2、自寫單鏈表
擴展:數據結構與算法:鏈表基礎 擴展:鏈表各類操作詳解代碼實現說明: 插入節點時,需要考慮多種情況,如輸入坐標不合法、插入到頭節點、在其他位置插入新節點等。插入方式為新建節點的下個節點指向頭節點,而后讓頭節點指向新建節點,完成插入。其他位置插入則是循環到坐標位置。 刪除節點時,也需要考慮多種情況,如輸入坐標坐標不合法、刪除頭節點、刪除其他節點等。刪除方式為頭節點找個替身,然后將頭節點指向頭節點的下個節點。其他位置刪除則是循環到坐標位置。 //實現單鏈表中的各種操作 #include <stdio.h> #include <stdlib.h>//動態分配內存 //定義節點的數據類型 typedef struct Node {int data;//數據內容,可以是其他的數據類型struct Node* next;//下一個節點地址 }Node; typedef struct {int cnt;//記錄節點個數Node* head;//頭節點 }List;//鏈表 //創建新節點的功能 Node* create_node(int data) {Node* pn=(Node*)malloc(sizeof(Node));pn->next=NULL;pn->data=data; } //向指定的位置插入指定的新節點 void insert(List* pl,int pos,int data) {//1.判斷pos坐標是否合法if(pos < 0 || pos > pl->cnt)//插入坐標不合法{//printf("插入坐標不合法,插入新節點失敗\n");//return;//pos=0;//默認插入到頭節點位置pos=pl->cnt;//插入到尾部}//2.創建新節點//Node* pn=(Node*)malloc(sizeof(Node));//pn->next=NULL;//pn->data=data;Node* pn=create_node(data);//3.當頭節點位置插入節點時if(0==pos){pn->next=pl->head;pl->head=pn;pl->cnt++;return;}//4.當向其他位置插入新節點時Node* p=pl->head;int i=0;for(i=1;i<pos;i++){p=p->next;//指向下一個}pn->next=p->next;p->next=pn;//節點個數加1pl->cnt++; } //向鏈表的頭節位置插入新節點 void push_head(List* pl,int data) {//創建新的節點//Node* pn=(Node*)malloc(sizeof(Node));//pn->next=NULL;//pn->data=data;//Node* pn=create_node(data);//插入新節點到鏈表頭部//pn->next=pl->head;//pl->head=pn;//pl->cnt++;insert(pl,0,data); } //遍歷鏈表的操作 void travel(List* pl) {Node* p=pl->head;if (NULL == p){printf ("鏈表已空\n");return ;}printf("鏈表中的元素有:\n");while(NULL!=p){printf("%d ",p->data);p=p->next;}printf("\n"); } //計算鏈表中節點個數 int size(List* pl) {return pl->cnt; } //判斷鏈表是否為空 int empty(List* pl) {return NULL==pl->head; } //判斷鏈表是否為滿 int full(List* pl) {return 0; } //向鏈表的尾部插入新節點 void push_tail(List* pl,int data) {insert(pl,pl->cnt,data); } //刪除鏈表中指定下標的節點 void del(List* pl,int pos) {//1.判斷坐標是否合法if(pos<0||pos>=pl->cnt)//注意其中有等于的情況{printf("坐標不合法,刪除節點失敗\n");return;}//2.當刪除頭節點時的處理方案if(0==pos){Node* p=pl->head;pl->head=pl->head->next;free(p);p=NULL;pl->cnt--;return;}//3.當刪除其他節點時的處理方法Node* p=pl->head;int i=0;for(i=1;i<pos;i++){p=p->next;}//下面代碼是pos=1時的功能代碼Node* q=p->next;p->next=p->next->next;free(q);q=NULL;pl->cnt--; } //清空鏈表中的所有節點 void clear(List* pl) {printf ("清空鏈表中的所有節點\n");while(pl->head!=NULL){//保存即將釋放的節點地址Node* p=pl->head;//頭指針指向下一個節點pl->head=pl->head->next;//釋放保存的節點free(p);p=NULL;}//將鏈表中節點元素的個數置為0pl->cnt=0; } int main() {//創建鏈表List list;list.head=NULL;list.cnt=0;push_head(&list,11);push_head(&list,22);push_head(&list,33);push_head(&list,44);travel(&list);insert(&list,3,100);travel(&list);insert(&list,3,66);travel(&list);insert(&list,3,55);travel(&list);insert(&list,-2,77);travel(&list);insert(&list,8,88);travel(&list);del(&list,9);travel(&list);del(&list,0);travel(&list);del(&list,3);travel(&list);printf("鏈表中元素個數是:%d\n",size(&list));printf("%s\n",full(&list)?"鏈表為滿":"鏈表未滿");printf("%s\n",empty(&list)?"鏈表為空":"鏈表未空");clear(&list);travel(&list);return 0; } 輸出結果: 鏈表中的元素有: 44 33 22 11 鏈表中的元素有: 44 33 22 100 11 鏈表中的元素有: 44 33 22 66 100 11 鏈表中的元素有: 44 33 22 55 66 100 11 鏈表中的元素有: 44 33 22 55 66 100 11 77 鏈表中的元素有: 44 33 22 55 66 100 11 77 88 坐標不合法,刪除節點失敗 鏈表中的元素有: 44 33 22 55 66 100 11 77 88 鏈表中的元素有: 33 22 55 66 100 11 77 88 鏈表中的元素有: 33 22 55 100 11 77 88 鏈表中元素個數是:7 鏈表未滿 鏈表未空 清空鏈表中的所有節點 鏈表已空
3、單向線性鏈表總結
每個節點除了存放元素數據之外,還需要保存指向下一個節點的指針,即所謂后指針。 鏈表尾節點的后指針為空指針。三、單向循環鏈表
參看:數據結構_線性表_鏈式存儲_單向循環鏈表的基本操作 對于一個循環鏈表來說其首節點和末節點被連接在一起。這種方式在單向和雙向鏈表中皆可實現。要轉換一個循環鏈表,可以選擇開始于任意一個節點然后沿著列表的任一方向直到返回開始的節點。再來看另一種方法,循環鏈表可以被視為“無頭無尾”。這種列表利于節約數據存儲緩存,假定你在一個列表中有一對象并且希望所有其他對象迭代在一個非特殊的排列下。 指向整個列表的指針可以被作為訪問指針。如下:代碼實現說明: 參看:一步一步寫算法(之循環單向鏈表) 插入節點時,需要考慮多種情況,如輸入坐標不合法、插入到頭節點、在其他位置插入新節點等。插入方式為新建節點的下個節點指向新建節點,而后讓頭節點指向新建節點,完成插入。其他位置插入則是循環到坐標位置。 和線性鏈表不同,如果鏈表無節點則新建節點下個節點不指向NULL,而是執行本身。 刪除節點時,也需要考慮多種情況,如輸入坐標坐標不合法、刪除頭節點、刪除其他節點等。刪除方式為頭節點找個替身,然后將頭節點指向頭節點的下個節點。其他位置刪除則是循環到坐標位置。 當刪除的只剩一個節點時,刪除后需要設置為NULL。
1、單向循環鏈表
#include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFESIBLE -1 #define OVERFLOW -2 typedef int Status; typedef int Boolean; typedef int ElemType;struct LNode { ElemType data; struct LNode *next; }; typedef struct LNode *LinkList; /* 另一種定義LinkList的方法 */ /* bo2-4.c 設立尾指針的單循環鏈表(存儲結構由c2-2.h定義)的12個基本操作 */ Status InitList_CL(LinkList *L) { /* 操作結果:構造一個空的線性表L */ *L=(LinkList)malloc(sizeof(struct LNode)); /* 產生頭結點,并使L指向此頭結點 */ if(!*L) /* 存儲分配失敗 */ exit(OVERFLOW); (*L)->next=*L; /* 指針域指向頭結點 * return OK; } Status DestroyList_CL(LinkList *L) { /* 操作結果:銷毀線性表L */ LinkList q,p=(*L)->next; /* p指向頭結點 */ while(p!=*L) /* 沒到表尾 */ { q=p->next; free(p); p=q; } free(*L); *L=NULL;/ return OK; } Status ClearList_CL(LinkList *L) /* 改變L */ { /* 初始條件:線性表L已存在。操作結果:將L重置為空表 */ LinkList p,q; *L=(*L)->next; /* L指向頭結點 */ p=(*L)->next; /* p指向第一個結點 */ while(p!=*L) /* 沒到表尾 */ { q=p->next; free(p); p=q; } (*L)->next=*L; /* 頭結點指針域指向自身 */ return OK; } Status ListEmpty_CL(LinkList L) { /* 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE */ if(L->next==L) /* 空 */ return TRUE; else return FALSE; } int ListLength_CL(LinkList L) { /* 初始條件:L已存在。操作結果:返回L中數據元素個數 */ int i=0; LinkList p=L->next; /* p指向頭結點 */ while(p!=L) /* 沒到表尾 */ { i++; p=p->next; } return i; } Status GetElem_CL(LinkList L,int i,ElemType *e) { /* 當第i個元素存在時,其值賦給e并返回OK,否則返回ERROR */ int j=1; /* 初始化,j為計數器 */ LinkList p=L->next->next; /* p指向第一個結點 */ if(i<=0||i>ListLength_CL(L)) /* 第i個元素不存在 */ return ERROR; while(j<i) { /* 順指針向后查找,直到p指向第i個元素 */ p=p->next; j++; } *e=p->data; /* 取第i個元素 */ return OK; } int LocateElem_CL(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) { /* 初始條件:線性表L已存在,compare()是數據元素判定函數 */ /* 操作結果:返回L中第1個與e滿足關系compare()的數據元素的位序。 */ /* 若這樣的數據元素不存在,則返回值為0 */ int i=0; LinkList p=L->next->next; /* p指向第一個結點 */ while(p!=L->next) { i++; if(compare(p->data,e)) /* 滿足關系 */ return i; p=p->next; } return 0; } Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType *pre_e) { /* 初始條件:線性表L已存在 */ /* 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅, */ /* 否則操作失敗,pre_e無定義 */ LinkList q,p=L->next->next; /* p指向第一個結點 */ q=p->next; while(q!=L->next) /* p沒到表尾 */ { if(q->data==cur_e) { *pre_e=p->data; return TRUE; } p=q; q=q->next; } return FALSE; } Status NextElem_CL(LinkList L,ElemType cur_e,ElemType *next_e) { /* 初始條件:線性表L已存在 */ /* 操作結果:若cur_e是L的數據元素,且不是最后一個,則用next_e返回它的后繼, */ /* 否則操作失敗,next_e無定義 */ LinkList p=L->next->next; /* p指向第一個結點 */ while(p!=L) /* p沒到表尾 */ { if(p->data==cur_e) { *next_e=p->next->data; return TRUE; } p=p->next; } return FALSE; } Status ListInsert_CL(LinkList *L,int i,ElemType e) /* 改變L */ { /* 在L的第i個位置之前插入元素e */ LinkList p=(*L)->next,s; /* p指向頭結點 */ int j=0; if(i<=0||i>ListLength_CL(*L)+1) /* 無法在第i個元素之前插入 */ return ERROR; while(j<i-1) /* 尋找第i-1個結點 */ { p=p->next; j++; } s=(LinkList)malloc(sizeof(struct LNode)); /* 生成新結點 */ s->data=e; /* 插入L中 */ s->next=p->next; p->next=s; if(p==*L) /* 改變尾結點 */ *L=s; return OK; } Status ListDelete_CL(LinkList *L,int i,ElemType *e) /* 改變L */ { /* 刪除L的第i個元素,并由e返回其值 */ LinkList p=(*L)->next,q; /* p指向頭結點 */ int j=0; if(i<=0||i>ListLength_CL(*L)) /* 第i個元素不存在 */ return ERROR; while(j<i-1) /* 尋找第i-1個結點 */ { p=p->next; j++; } q=p->next; /* q指向待刪除結點 */ p->next=q->next; *e=q->data; if(*L==q) /* 刪除的是表尾元素 */ *L=p; free(q); /* 釋放待刪除結點 */ return OK; } Status ListTraverse_CL(LinkList L) { /* 初始條件:L已存在。操作結果:依次對L的每個數據元素調用函數vi()。一旦vi()失敗,則操作失敗 */ LinkList p=L->next->next; while(p!=L->next) { printf("%d ",p->data); p=p->next; } printf("\n"); return OK; } Status compare(ElemType c1,ElemType c2) { if(c1==c2) return TRUE; else return FALSE; } int main() { LinkList L; ElemType e; int j; Status i; i = InitList_CL(&L); printf("初始化單循環鏈表L i=%d(1:初始化成功)\n", i); i=ListEmpty_CL(L); printf("L是否空 i=%d(1:空 0:否)\n",i); ListInsert_CL(&L,1,3); /* 在L中依次插入3,5 */ ListInsert_CL(&L,2,5); i=GetElem_CL(L,1,&e); j=ListLength_CL(L); printf("L中數據元素個數=%d,第1個元素的值為%d。\n",j,e); printf("L中的數據元素依次為:"); ListTraverse_CL(L); PriorElem_CL(L,5,&e); /* 求元素5的前驅 */ printf("5前面的元素的值為%d。\n",e); NextElem_CL(L,3,&e); /* 求元素3的后繼 */ printf("3后面的元素的值為%d。\n",e); printf("L是否空 %d(1:空 0:否)\n",ListEmpty_CL(L)); j=LocateElem_CL(L,5,compare); if(j) printf("L的第%d個元素為5。\n",j); else printf("不存在值為5的元素\n"); i=ListDelete_CL(&L,2,&e); printf("刪除L的第2個元素:\n"); if(i) { printf("刪除的元素值為%d,現在L中的數據元素依次為:",e); ListTraverse_CL(L); } else printf("刪除不成功!\n"); printf("清空L:%d(1: 成功)\n",ClearList_CL(&L)); printf("清空L后,L是否空:%d(1:空 0:否)\n",ListEmpty_CL(L)); printf("銷毀L:%d(1: 成功)\n",DestroyList_CL(&L)); return 0; } 輸出結果: 初始化單循環鏈表L i=1(1:初始化成功) L是否空 i=1(1:空 0:否) L中數據元素個數=2,第1個元素的值為3。 L中的數據元素依次為:3 5 5前面的元素的值為3。 3后面的元素的值為5。 L是否空 0(1:空 0:否) L的第2個元素為5。 刪除L的第2個元素: 刪除的元素值為5,現在L中的數據元素依次為:3 清空L:1(1: 成功) 清空L后,L是否空:1(1:空 0:否) 銷毀L:1(1: 成功)2、再例
#include <stdio.h> #include <stdlib.h>/*存儲結構的定義*/ typedef struct CLinkList {int data ;struct CLinkList * next ; }node;/************************************************************************/ /* 操作 */ /************************************************************************//*初始化循環鏈表*/ void ds_init(node ** pNode) {int item ;node * temp ;node * target ;printf("輸入結點的值,輸入0完成初始化\n");while(1) {scanf("%d",&item) ;fflush(stdin) ;if(item == 0)return ;if((*pNode) == NULL) { /*循環鏈表中只有一個結點*/*pNode = (node*)malloc(sizeof(struct CLinkList)) ;if(!(*pNode))exit(0) ;(*pNode)->data = item ;(*pNode)->next = *pNode ;}else {/*找到next指向第一個結點的結點*/for(target = (*pNode) ; target->next != (*pNode) ; target = target->next) ;/*生成一個新的結點*/temp = (node *) malloc(sizeof(struct CLinkList)) ;if(!temp)exit(0) ;temp->data = item ;temp->next = *pNode ;target->next = temp ;}} }/*插入結點*/ /*參數:鏈表的第一個結點,插入的位置*/ void ds_insert(node ** pNode ,int i) {node * temp ;node * target ;node * p ;int item ;int j = 1 ;printf("輸入要插入結點的值:");scanf("%d",&item) ;if(i == 1) { //新插入的結點作為第一個結點temp = (node *)malloc(sizeof(struct CLinkList)) ;if(!temp)exit(0) ;temp ->data = item ;/*尋找到最后一個結點*/for(target = (*pNode) ; target->next != (*pNode) ; target = target->next) ;temp->next = (*pNode) ;target->next = temp ;*pNode = temp ;}else {target = *pNode ;for( ; j < (i-1) ; target=target->next,++ j) ;temp = (node *)malloc(sizeof(struct CLinkList)) ;if(!temp)exit(0) ;temp ->data = item ;p = target->next ;target->next = temp ;temp->next = p ;} }/*刪除結點*/ void ds_delete(node ** pNode,int i) {node * target ;node * temp ;int j = 1 ;if(i == 1) { //刪除的是第一個結點/*找到最后一個結點*/for(target = *pNode ; target->next != *pNode ;target = target->next) ;temp = *pNode ;*pNode = (*pNode)->next ;target->next = *pNode ;free(temp) ;}else {target = *pNode ;for( ; j < i-1 ; target= target->next,++j) ;temp = target->next ;target->next = temp->next ;free(temp) ;} }/*返回結點所在位置*/ int ds_search(node * pNode,int elem) {node * target ;int i = 1 ;for(target = pNode ; target->data != elem && target->next != pNode ; ++i , target = target->next) ;if(target->next == pNode) /*表中不存在該元素*/return 0 ;elsereturn i ; } /*遍歷*/ void ds_traverse(node * pNode) {node * temp ;temp = pNode ;printf("***********鏈表中的元素******************\n");do {printf("%4d ",temp->data) ;}while((temp = temp->next) != pNode) ;printf("\n") ; }int main() {node * pHead = NULL ;char opp;int find;printf("\n1.初始化鏈表 \n2.插入結點 \n3.刪除結點 \n4.返回結點位置 \n5.遍歷鏈表 \n0.退出 \n請選擇你的操作:\n");while(opp != '0'){scanf("%c",&opp);switch(opp){case '1':ds_init(&pHead);printf("\n");ds_traverse(pHead) ;break;case '2':printf("輸入需要插入結點的位置?");scanf("%d", &find);ds_insert(&pHead,find) ;printf("在位置%d插入值后:\n", find) ;ds_traverse(pHead) ;printf("\n");break;case '3':printf("輸入需要刪除的結點位置?");scanf("%d", &find);ds_delete(&pHead,find) ;printf("刪除第%d個結點后:\n", find) ;ds_traverse(pHead) ;printf("\n");break;case '4':printf("你要查找倒數第幾個結點的值?");scanf("%d", &find);printf("元素%d所在位置:%d\n", find, ds_search(pHead,find)) ;//ListTraverse(L);printf("\n");break;case '5':ds_traverse(pHead) ;printf("\n");break;case '0':exit(0);}}return 0 ; }3、又例
//循環鏈表是一種手尾相接的鏈表,在單鏈表中,如果最后一個結點的指針域不指向空,而是指向頭結點,整個鏈表就形成一個環,這是另一種的鏈式存儲結構,成為單循環鏈表,類似的還有多重循環鏈表 //為了使空表和非空表的處理一致,同樣設置了一個頭結點 //創建頭結點后,應有h->next=h語句 //特點1.從任意結點出發可以很容易的找到其他的結點,在某些算法上易于實現 //特點2.單循環鏈表的操作和實現和單鏈表的上基本一致,但注意的是算法的循環條件p或p->next 是否為空改為是否等于頭指針#include <stdio.h> #include "stdlib.h"typedef int elemetType; typedef struct Node {elemetType data;struct Node* next;}cNode,*circleList;#pragma mark --創建有n個結點的循環鏈表 cNode* create(int n) {cNode*cNew,*cHead,*cTail; //聲明三個鏈表cHead=(cNode*)malloc(sizeof(cNode));//頭結點申請空間cHead->next=cHead; // 空鏈表指向自身cTail=cHead; //cTail的指針指向cHeadint i = 0;for (i=0; i<n; i++) {cNew=malloc(sizeof(cNode));if (cNew==NULL) {printf("errpr\n");exit(0);}cNew->data=i;if (cHead->next==cHead) {//此時為空鏈表cHead->next=cNew;cTail=cNew; //cTail指向新添加的結點}else { //不是空鏈表的時候cTail->next=cNew;cTail=cNew;}}cTail->next=cHead;//添加完畢后,把尾指針的指針域指向頭結點return cTail; }#pragma mark --單循環鏈表的遍歷 void traverse(cNode*c) {cNode* tempNode;tempNode=c;printf("------鏈表中的元素--------\n");printf("%4d ",tempNode->data) ;do {printf("%4d ",tempNode->data) ;}while((tempNode = tempNode->next) != c);printf("\n") ;printf("\n") ;}#pragma mark --在單循環列表查找值為x的結點 cNode* get(cNode*h,elemetType x) {cNode*p;p=h->next;while (p!=h&&p->data!=x)p=p->next;if (p==h) {printf("----不好意思,您轉了一圈還是沒有找到----\n");return NULL;}printf("--找到了--\n");return p; }#pragma mark 返回結點所處的位置 int searchPosition(cNode * pNode,int elem) {cNode * target ;int i = 1 ;for(target = pNode ; target->data != elem && target->next != pNode ; ++i , target = target->next) ;if(target->next == pNode) /*表中不存在該元素*/return 0 ;elsereturn i ; }int main(int argc, const char * argv[]) {// insert code here...cNode*c=create(20);traverse(c);get(c, 40);get(c, 10);int position=searchPosition(c, 6);printf("position====%d\n",position);///????為什么等于9return 0; } 輸出結果: ------鏈表中的元素--------19 19 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ----不好意思,您轉了一圈還是沒有找到---- --找到了-- position====94、單向循環鏈表總結
每個節點除了存放元素數據之外,還需要保存指向下一個節點的指針,即所謂后指針。 鏈表尾節點的后指針指向首節點,首尾相接構成環狀。四、雙向線性鏈表
參看:C語言雙向鏈表的表示與實現實例詳解 C語言中一種更復雜的鏈表式“雙向鏈表”或“雙面鏈表”。其表中的每個節點有兩個連接:一個指向前一個節點,(當這個“連接”為第一個:“連接”時,指向空值或者空列表);而另一個指向下一個節點,(當這個“連接”為最后一個:“連接”時,指向空值或者空列表)。例如:在一些低級語言中,XOR-linking 提供一種在雙向鏈表中通過用一個詞來表示兩個鏈接(前后),我們通常不提倡這種方法. 雙向鏈表也叫雙鏈表。雙向鏈表中不僅有指向后一個節點的指針,還有指向前一個節點的指針。這樣就可以從任何一個節點訪問前一個節點,當然也可以訪問后一個節點,以至于整個鏈表。一般是在需要大批量的另外儲存數據在鏈表中的位置的時候用。雙向鏈表也可以配合下面的其他鏈表的擴展使用。 由于另外儲存了指向鏈表內容的指針,并且可能會修改相鄰的節點,有的時候第一個節點可能會被刪除或者之前添加一個新的節點。這時候就要修改指向首個節點的指針。有一種方便的可以消除這種特殊情況的方法是在最后一個節點之后、第一個節點之前儲存一個永遠不會被刪除或移動的虛擬節點,形成一個下面說的循環鏈表。這個虛擬節點之后的節點就是真正的第一個節點。這種情況通常可以用這個虛擬節點直接表示這個鏈表,對于把鏈表單獨的存在數據里的情況,也可以直接用這個數組表示鏈表并用第 0 個或者第 -1 個 (如果編譯器支持)節點固定的表示這個虛擬節點。
1、雙向線性鏈表
參看:(C語言版)鏈表(三)——實現雙向鏈表創建、刪除、插入、釋放內存等簡單操作 參看:鏈表(單向、雙向、單向循環、雙向循環)學習過程總結——有源代碼、注釋和示意圖 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *pNext; struct Node *pPre; }NODE, *pNODE; //創建雙向鏈表 pNODE CreateDbLinkList(void); //打印鏈表 void TraverseDbLinkList(pNODE pHead); //判斷鏈表是否為空 int IsEmptyDbLinkList(pNODE pHead); //計算鏈表長度 int GetLengthDbLinkList(pNODE pHead); //向鏈表插入節點 int InsertEleDbLinkList(pNODE pHead, int pos, int data); //從鏈表刪除節點 int DeleteEleDbLinkList(pNODE pHead, int pos); //刪除整個鏈表,釋放內存 void FreeMemory(pNODE *ppHead); int main(void) { int flag = 0, length = 0; int position = 0, value = 0; pNODE head = NULL; head = CreateDbLinkList(); flag = IsEmptyDbLinkList(head); if (flag) printf("雙向鏈表為空!\n"); else { length = GetLengthDbLinkList(head); printf("雙向鏈表的長度為:%d\n", length); TraverseDbLinkList(head); } printf("請輸入要插入節點的位置和元素值(兩個數用空格隔開):"); scanf("%d %d", &position, &value); flag = InsertEleDbLinkList(head, position, value); if (flag) { printf("插入節點成功!\n"); TraverseDbLinkList(head); } else printf("插入節點失敗!\n"); flag = IsEmptyDbLinkList(head); if (flag) printf("雙向鏈表為空,不能進行刪除操作!\n"); else { printf("請輸入要刪除節點的位置:"); scanf("%d", &position); flag = DeleteEleDbLinkList(head, position); if (flag) { printf("刪除節點成功!\n"); TraverseDbLinkList(head); } else printf("刪除節點失敗!\n"); } FreeMemory(&head); if (NULL == head) printf("已成功刪除雙向鏈表,釋放內存完成!\n"); else printf("刪除雙向鏈表失敗,釋放內存未完成!\n"); return 0; } //創建雙向鏈表 pNODE CreateDbLinkList(void) { int i, length = 0, data = 0; pNODE pTail = NULL, p_new = NULL; pNODE pHead = (pNODE)malloc(sizeof(NODE)); if (NULL == pHead) { printf("內存分配失敗!\n"); exit(EXIT_FAILURE); } pHead->data = 0; pHead->pPre = NULL; pHead->pNext = NULL; pTail = pHead; printf("請輸入想要創建鏈表的長度:"); scanf("%d", &length); for (i=1; i<length+1; i++) { p_new = (pNODE)malloc(sizeof(NODE)); if (NULL == p_new) { printf("內存分配失敗!\n"); exit(EXIT_FAILURE); } printf("請輸入第%d個元素的值:", i); scanf("%d", &data); p_new->data = data; p_new->pNext = NULL; p_new->pPre = pTail; pTail->pNext = p_new; pTail = p_new; } return pHead; } //打印鏈表 void TraverseDbLinkList(pNODE pHead) { pNODE pt = pHead->pNext; printf("打印鏈表如:"); while (pt != NULL) { printf("%d ", pt->data); pt = pt->pNext; } putchar('\n'); } //判斷鏈表是否為空 int IsEmptyDbLinkList(pNODE pHead) { pNODE pt = pHead->pNext; if (pt == NULL) return 1; else return 0; } //計算鏈表的長度 int GetLengthDbLinkList(pNODE pHead) { int length = 0; pNODE pt = pHead->pNext; while (pt != NULL) { length++; pt = pt->pNext; } return length; } //向雙向鏈表中插入節點 int InsertEleDbLinkList(pNODE pHead, int pos, int data) { pNODE pt = NULL, p_new = NULL; if (pos > 0 && pos < GetLengthDbLinkList(pHead)+2) { p_new = (pNODE)malloc(sizeof(NODE)); if (NULL == p_new) { printf("內存分配失敗!\n"); exit(EXIT_FAILURE); } while (1) { pos--; if (0 == pos) break; pHead = pHead->pNext; } pt = pHead->pNext; p_new->data = data; p_new->pNext = pt; if (NULL != pt) pt->pPre = p_new; p_new->pPre = pHead; pHead->pNext = p_new; return 1; } else return 0; } //從鏈表中刪除節點 int DeleteEleDbLinkList(pNODE pHead, int pos) { pNODE pt = NULL; if (pos > 0 && pos < GetLengthDbLinkList(pHead) + 1) { while (1) { pos--; if (0 == pos) break; pHead = pHead->pNext; } pt = pHead->pNext->pNext; free(pHead->pNext); pHead->pNext = pt; if (NULL != pt) pt->pPre = pHead; return 1; } else return 0; } //刪除整個鏈表,釋放內存 void FreeMemory(pNODE *ppHead) { pNODE pt = NULL; while (*ppHead != NULL) { pt = (*ppHead)->pNext; free(*ppHead); if (NULL != pt) pt->pPre = NULL; *ppHead = pt; } } 輸出結果: 請輸入想要創建鏈表的長度:5 請輸入第1個元素的值:1 請輸入第2個元素的值:2 請輸入第3個元素的值:3 請輸入第4個元素的值:4 請輸入第5個元素的值:5 雙向鏈表的長度為:5 打印鏈表如:1 2 3 4 5 請輸入要插入節點的位置和元素值(兩個數用空格隔開):2 6 插入節點成功! 打印鏈表如:1 6 2 3 4 5 請輸入要刪除節點的位置:2 刪除節點成功! 打印鏈表如:1 2 3 4 5 已成功刪除雙向鏈表,釋放內存完成!2、自寫雙向線性鏈表
代碼實現說明: 插入節點時,首先判斷要插入的節點的位置是否正確,然后創建節點。最后,插入節點,插入時分兩種情況處理,一種是插入到第一個節點前面,另一種是插入到鏈表中間。創建持有待插入元素的新節點,令其前指針指向給定節點的前節點,令其后指針指向給定節點。 若新建節點存在前節點,則令其前節點的后指針指向新建節點,否則新建節點為新的首節點。 令新建節點的后節點的前指針指向新建節點。
刪除節點時,首先判斷要刪除的結點位置是否正確。然后,刪除結點,刪除時分兩種情況處理:一種是刪除第一個結點,另一種是刪除鏈表的中間結點。最后,釋放被刪除結點所占有的存儲空間。
若將亡節點存在前節點,則令其前節點的后指針指向將亡節點的后節點,否則將亡節點的后節點為新的首節點。 若將亡節點存在后節點,則令其后節點的前指針指向將亡節點的前節點,否則將亡節點的前節點為新的尾節點。 銷毀將亡節點。
#include <stdio.h> #include <stdbool.h> #include <stdlib.h>typedef int DataType; struct Node{DataType data;struct Node *pre, *next; };void init(struct Node** head) {*head = NULL; }int getSize(struct Node* head) {struct Node* p = head;int count = 0;while(p){count ++;p = p->next;}return count; }//找到指定位置 元素地址 struct Node* getptr(struct Node* head, int pos) {struct Node *p = head;if (p == 0 || pos == 0) {return head;}int i = 0;for(i = 0; p && i < pos; i++) {p = p->next;}return p; }//指定位置 插入元素 bool insert(struct Node** head, int position, DataType d) {if (position < 0 || position > getSize(*head)) {return false;}//創建 節點struct Node *node = (struct Node*)malloc(sizeof(struct Node));node->data = d;node->pre = NULL;node->next = NULL;//插入到第一個節點的前面if (position == 0) {node->next = *head;if (*head != NULL)(*head)->pre = node;*head = node;return true;}//插入到鏈表的中間struct Node *p = getptr(*head, position - 1);struct Node* r = p->next;node->next = r;r->pre = node;p->next = node;node->pre = p;return true; }//刪除指定位置元素 bool erases(struct Node** head, int pos) {if (pos < 0 || pos >= getSize(*head))return false;//刪除第一個結點struct Node *p = *head;if (pos == 0) {*head = (*head)->next;if (*head != NULL)(*head)->pre = NULL;free(p);p = NULL;return true;}//刪除鏈表的中間結點p = getptr(*head, pos - 1);struct Node *q = p->next;p->next = q->next;q->next->pre = p;free(q);q = NULL;return true; }//修改指定位置 元素 bool set(struct Node* head, int pos, DataType d) {if (pos < 0 || pos >= getSize(head)) {return false;}struct Node *p = getptr(head, pos);p->data = d;return true; }//清理 鏈表 void clears(struct Node* head) {while (head) {struct Node *p = head->next;free(head);head = p;} }//打印 void print(struct Node* head) {struct Node *p = head;while (p) {printf("%d ", p->data);p = p->next;}printf("\n"); }int main() {//頭指針struct Node *headList;init(&headList);insert(&headList, 0, 10);insert(&headList, 0, 20);insert(&headList, 0, 30);insert(&headList, 2, 40);insert(&headList, 2, 50);insert(&headList, 0, 60);insert(&headList, 0, 80);print(headList);erases(&headList, 1);print(headList);set(headList, 0, 100);set(headList, 0, 110);print(headList);return 0; } 輸出結果: 80 60 30 20 50 40 10 80 30 20 50 40 10 110 30 20 50 40 10
3、雙向線性鏈表總結
每個節點除了存放元素數據之外,還需要保存指向下一個節點的指針,即所謂后指針,以及指向前一個節點的指針,即所謂前指針。 鏈表首節點的前指針和尾節點的后指針俱為空指針。五、雙向循環鏈表
1、雙向循環鏈表
參看:(C語言版)鏈表(四)——實現雙向循環鏈表創建、插入、刪除、釋放內存等簡單操作 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *pNext; struct Node *pPre; }NODE, *pNODE; //創建雙向循環鏈表 pNODE CreateDbCcLinkList(void); //打印鏈表 void TraverseDbCcLinkList(pNODE pHead); //判斷鏈表是否為空 int IsEmptyDbCcLinkList(pNODE pHead); //計算鏈表的長度 int GetLengthDbCcLinkList(pNODE pHead); //向鏈表中插入節點 int InsertEleDbCcLinkList(pNODE pHead, int pos, int data); //從鏈表中刪除節點 int DeleteEleDbCcLinkList(pNODE pHead, int pos); //刪除整個鏈表,釋放內存 void FreeMemory(pNODE *ppHead); int main(void) { int flag = 0, length = 0; int position = 0, value = 0; pNODE head = NULL; head = CreateDbCcLinkList(); flag = IsEmptyDbCcLinkList(head); if (flag) printf("雙向循環鏈表為空!\n"); else { length = GetLengthDbCcLinkList(head); printf("雙向循環鏈表的長度為:%d\n", length); TraverseDbCcLinkList(head); } printf("請輸入要插入節點的位置和元素值(兩個數用空格隔開):"); scanf("%d %d", &position, &value); flag = InsertEleDbCcLinkList(head, position, value); if (flag) { printf("插入節點成功!\n"); TraverseDbCcLinkList(head); } else printf("插入節點失敗!\n"); flag = IsEmptyDbCcLinkList(head); if (flag) printf("雙向循環鏈表為空,不能進行刪除操作!\n"); else { printf("請輸入要刪除節點的位置:"); scanf("%d", &position); flag = DeleteEleDbCcLinkList(head, position); if (flag) { printf("刪除節點成功!\n"); TraverseDbCcLinkList(head); } else printf("刪除節點失敗!\n"); } FreeMemory(&head); if (NULL == head) printf("已成功刪除雙向循環鏈表,釋放內存完成!\n"); else printf("刪除雙向循環鏈表失敗,釋放內存未完成!\n"); return 0; } //創建雙向循環鏈表 pNODE CreateDbCcLinkList(void) { int i, length = 0, data = 0; pNODE p_new = NULL, pTail = NULL; pNODE pHead = (pNODE)malloc(sizeof(NODE)); if (NULL == pHead) { printf("內存分配失敗!\n"); exit(EXIT_FAILURE); } pHead->data = 0; pHead->pNext = pHead; pHead->pPre = pHead; pTail = pHead; printf("請輸入想要創建鏈表的長度:"); scanf("%d", &length); for (i=1; i<length+1; i++) { p_new = (pNODE)malloc(sizeof(NODE)); if (NULL == p_new) { printf("內存分配失敗!\n"); exit(EXIT_FAILURE); } printf("請輸入第%d個節點元素值:", i); scanf("%d", &data); p_new->data = data; p_new->pPre = pTail; p_new->pNext = pHead; pTail->pNext = p_new; pHead->pPre = p_new; pTail = p_new; } return pHead; } //打印鏈表 void TraverseDbCcLinkList(pNODE pHead) { pNODE pt = pHead->pNext; printf("鏈表打印如:"); while (pt != pHead) { printf("%d ", pt->data); pt = pt->pNext; } putchar('\n'); } //判斷鏈表是否為空 int IsEmptyDbCcLinkList(pNODE pHead) { pNODE pt = pHead->pNext; if (pt == pHead) return 1; else return 0; } //計算鏈表的長度 int GetLengthDbCcLinkList(pNODE pHead) { int length = 0; pNODE pt = pHead->pNext; while (pt != pHead) { length++; pt = pt->pNext; } return length; } //向鏈表中插入節點 int InsertEleDbCcLinkList(pNODE pHead, int pos, int data) { pNODE p_new = NULL, pt = NULL; if (pos > 0 && pos < GetLengthDbCcLinkList(pHead) + 2) { p_new = (pNODE)malloc(sizeof(NODE)); if (NULL == p_new) { printf("內存分配失敗!\n"); exit(EXIT_FAILURE); } while (1) { pos--; if (0 == pos) break; pHead = pHead->pNext; } p_new->data = data; pt = pHead->pNext; p_new->pNext = pt; p_new->pPre = pHead; pHead->pNext = p_new; pt->pPre = p_new; return 1; } else return 0; } //從鏈表中刪除節點 int DeleteEleDbCcLinkList(pNODE pHead, int pos) { pNODE pt = NULL; if (pos > 0 && pos < GetLengthDbCcLinkList(pHead) + 1) { while (1) { pos--; if (0 == pos) break; pHead = pHead->pNext; } pt = pHead->pNext->pNext; free(pHead->pNext); pHead->pNext = pt; pt->pPre = pHead; return 1; } else return 0; } //刪除整個鏈表,釋放內存空間 void FreeMemory(pNODE *ppHead) { pNODE pt = NULL; while (*ppHead != NULL) { pt = (*ppHead)->pNext->pNext; if ((*ppHead)->pNext == *ppHead) { free(*ppHead); *ppHead = NULL; } else { free((*ppHead)->pNext); (*ppHead)->pNext = pt; pt->pPre = *ppHead; } } } 輸出結果: 請輸入想要創建鏈表的長度:5 請輸入第1個節點元素值:1 請輸入第2個節點元素值:2 請輸入第3個節點元素值:3 請輸入第4個節點元素值:4 請輸入第5個節點元素值:5 雙向循環鏈表的長度為:5 鏈表打印如:1 2 3 4 5 請輸入要插入節點的位置和元素值(兩個數用空格隔開):2 6 插入節點成功! 鏈表打印如:1 6 2 3 4 5 請輸入要刪除節點的位置:2 刪除節點成功! 鏈表打印如:1 2 3 4 5 已成功刪除雙向循環鏈表,釋放內存完成!2、雙向循環鏈表
參看:數據結構_線性表_鏈式存儲_雙向循環鏈表的基本操作 #include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef int Status; typedef int ElemType; /* 線性表的雙向鏈表存儲結構 */ typedef struct DuLNode {ElemType data;struct DuLNode *prior,*next; }DuLNode,*DuLinkList;/* 雙鏈循環線性表的基本操作(14個) */ Status InitList(DuLinkList *L) { /* 產生空的雙向循環鏈表L */*L=(DuLinkList)malloc(sizeof(DuLNode));if(*L){(*L)->next=(*L)->prior=*L;return OK;}elsereturn OVERFLOW; }Status DestroyList(DuLinkList *L) { /* 操作結果:銷毀雙向循環鏈表L */DuLinkList q,p=(*L)->next; /* p指向第一個結點 */while(p!=*L) /* p沒到表頭 */{q=p->next;free(p);p=q;}free(*L);*L=NULL;return OK; }Status ClearList(DuLinkList L) /* 不改變L */ { /* 初始條件:L已存在。操作結果:將L重置為空表 */DuLinkList q,p=L->next; /* p指向第一個結點 */while(p!=L) /* p沒到表頭 */{q=p->next;free(p);p=q;}L->next=L->prior=L; /* 頭結點的兩個指針域均指向自身 */return OK; }Status ListEmpty(DuLinkList L) { /* 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE */if(L->next==L&&L->prior==L)return TRUE;elsereturn FALSE; }int ListLength(DuLinkList L) { /* 初始條件:L已存在。操作結果:返回L中數據元素個數 */int i=0;DuLinkList p=L->next; /* p指向第一個結點 */while(p!=L) /* p沒到表頭 */{i++;p=p->next;}return i; }Status GetElem(DuLinkList L,int i,ElemType *e) { /* 當第i個元素存在時,其值賦給e并返回OK,否則返回ERROR */int j=1; /* j為計數器 */DuLinkList p=L->next; /* p指向第一個結點 */while(p!=L&&j<i) /* 順指針向后查找,直到p指向第i個元素或p指向頭結點 */{p=p->next;j++;}if(p==L||j>i) /* 第i個元素不存在 */return ERROR;*e=p->data; /* 取第i個元素 */return OK; }int LocateElem(DuLinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) { /* 初始條件:L已存在,compare()是數據元素判定函數 *//* 操作結果:返回L中第1個與e滿足關系compare()的數據元素的位序。 *//* 若這樣的數據元素不存在,則返回值為0 */int i=0;DuLinkList p=L->next; /* p指向第1個元素 */while(p!=L){i++;if(compare(p->data,e)) /* 找到這樣的數據元素 */return i;p=p->next;}return 0; }Status PriorElem(DuLinkList L,ElemType cur_e,ElemType *pre_e) { /* 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅, *//* 否則操作失敗,pre_e無定義 */DuLinkList p=L->next->next; /* p指向第2個元素 */while(p!=L) /* p沒到表頭 */{if(p->data==cur_e){*pre_e=p->prior->data;return TRUE;}p=p->next;}return FALSE; }Status NextElem(DuLinkList L,ElemType cur_e,ElemType *next_e) { /* 操作結果:若cur_e是L的數據元素,且不是最后一個,則用next_e返回它的后繼, *//* 否則操作失敗,next_e無定義 */DuLinkList p=L->next->next; /* p指向第2個元素 */while(p!=L) /* p沒到表頭 */{if(p->prior->data==cur_e){*next_e=p->data;return TRUE;}p=p->next;}return FALSE; }DuLinkList GetElemP(DuLinkList L,int i) /* 另加 */ { /* 在雙向鏈表L中返回第i個元素的位置指針(算法2.18、2.19要調用的函數) */int j;DuLinkList p=L;for(j=1;j<=i;j++)p=p->next;return p; }Status ListInsert(DuLinkList L,int i,ElemType e) /* 改進算法2.18 */ { /* 在帶頭結點的雙鏈循環線性表L中第i個位置之前插入元素e,i的合法值為1≤i≤表長+1 */DuLinkList p,s;if(i<1||i>ListLength(L)+1) /* i值不合法 */return ERROR;p=GetElemP(L,i-1); /* 在L中確定第i-1個元素的位置指針p */if(!p) /* p=NULL,即第i-1個元素不存在 */return ERROR;s=(DuLinkList)malloc(sizeof(DuLNode));if(!s)return OVERFLOW;s->data=e; /* 在第i-1個元素之后插入 */s->prior=p;s->next=p->next;p->next->prior=s;p->next=s;return OK; }Status ListDelete(DuLinkList L,int i,ElemType *e) /* 算法2.19 */ { /* 刪除帶頭結點的雙鏈循環線性表L的第i個元素,i的合法值為1≤i≤表長+1 */DuLinkList p;if(i<1||i>ListLength(L)) /* i值不合法 */return ERROR;p=GetElemP(L,i); /* 在L中確定第i個元素的位置指針p */if(!p) /* p=NULL,即第i個元素不存在 */return ERROR;*e=p->data;p->prior->next=p->next;p->next->prior=p->prior;free(p);return OK; }void visit(ElemType c) {printf("%d ",c); }void ListTraverse(DuLinkList L,void(*visit)(ElemType)) { /* 由雙鏈循環線性表L的頭結點出發,正序對每個數據元素調用函數visit() */DuLinkList p=L->next; /* p指向頭結點 */while(p!=L){visit(p->data);p=p->next;}printf("\n"); }void ListTraverseBack(DuLinkList L,void(*visit)(ElemType)) { /* 由雙鏈循環線性表L的頭結點出發,逆序對每個數據元素調用函數visit()。另加 */DuLinkList p=L->prior; /* p指向尾結點 */while(p!=L){visit(p->data);p=p->prior;}printf("\n"); }Status compare(ElemType c1,ElemType c2) { if(c1==c2) return TRUE; else return FALSE; } int main(int argc, const char * argv[]) {// insert code here...DuLinkList L;ElemType e;int j;Status i;i = InitList(&L); /* 初始化單循環鏈表L */printf("初始化雙向循環鏈表L i=%d(1:初始化成功)\n", i); i=ListEmpty(L); printf("L是否空 i=%d(1:空 0:否)\n",i); ListInsert(L, 1, 15);ListInsert(L, 2, 25);ListInsert(L, 3, 35);ListInsert(L, 4, 45);i=GetElem(L,1,&e); j=ListLength(L); printf("L中數據元素個數=%d,第1個元素的值為%d。\n",j,e); printf("L中的數據元素依次為:"); ListTraverse(L,visit);PriorElem(L,25,&e); /* 求元素5的前驅 */printf("25前面的元素的值為%d。\n",e);NextElem(L,35,&e); /* 求元素3的后繼 */printf("35后面的元素的值為%d。\n",e);printf("L是否空 %d(1:空 0:否)\n",ListEmpty(L));j=LocateElem(L,45,compare); if(j) printf("L的第%d個元素為45。\n",j); else printf("不存在值為45的元素\n"); //刪除第二個元素ListDelete(L, 2, &e);printf("刪除L的第2個元素:\n"); if(i) { printf("刪除的元素值為%d,現在L中的數據元素依次為:",e); ListTraverse(L,visit); } else printf("刪除不成功!\n"); j=ListLength(L);printf("L中數據元素個數=%d\n",j);ListTraverse(L,visit);ListTraverseBack (L,visit);printf("清空L:%d(1: 成功)\n",ClearList(L)); printf("清空L后,L是否空:%d(1:空 0:否)\n",ListEmpty(L)); printf("銷毀L:%d(1: 成功)\n",DestroyList(&L)); return 0; } 輸出結果: 初始化雙向循環鏈表L i=1(1:初始化成功) L是否空 i=1(1:空 0:否) L中數據元素個數=4,第1個元素的值為15。 L中的數據元素依次為:15 25 35 45 25前面的元素的值為15。 35后面的元素的值為45。 L是否空 0(1:空 0:否) L的第4個元素為45。 刪除L的第2個元素: 刪除的元素值為25,現在L中的數據元素依次為:15 35 45 L中數據元素個數=3 15 35 45 45 35 15 清空L:1(1: 成功) 清空L后,L是否空:1(1:空 0:否) 銷毀L:1(1: 成功)3、雙向循環鏈表總結
每個節點除了存放元素數據之外,還需要保存指向下一個節點的指針,即所謂后指針,以及指向前一個節點的指針,即所謂前指針。 鏈表首節點的前指針和尾節點的后指針分別指向鏈表的尾節點和首節點。六、數組鏈表
這部分只做了解好了,使用數組鏈表效率不高。1、數組鏈表
參看:用數組實現鏈表(C++) 參看:數組作鏈表 #include<iostream> using namespace std; class List{ private: int maxSize; int n; int *list; public: List(int max); ~List(){delete []list;} bool isEmpty(){return n==0;} int length(){return n;} int locate(int &x);//返回表中元素x的位置 bool retrieve(int k, int &x);//返回表中位置k,將之放入元素x List& insert(int k,int x);//在位置k插入元素x List& Delete(int k,int &x);//刪除位置k的元素,將之存在x中] void printList(); }; List::List(int max){ maxSize = max; n = 0; list = new int[maxSize]; } int List::locate(int &x){ for(int i=0;i<n;i++) if(list[i]==x) return i; return -1; } bool List::retrieve(int k, int &x){ if(k<1||k>n) return false; x = list[k]; return true; } List& List::insert(int k, int x){ //if(k<0||k>n) 此處應拋出異常 //if(n==maxSize) 此處應拋出異常 for(int i = n-1; i >= k; i++) list[i+1] = list[i]; list[k] = x; n++; return *this; } List& List::Delete(int k, int &x){ if(retrieve(k, x)){ for(int i = k; i < n; i++) list[i] = list[i+1]; n--; return *this; } //else 在此拋出異常 } void List::printList() { for(int i=0; i < n; i++) cout<<list[i]<<" "; cout<<endl; } int main() { List list(10); list.insert(0,1); list.insert(1,2); list.insert(2,3); int listLength = list.length(); list.printList(); cout<<"數組鏈表的長度為"<<listLength<<endl; int delElement = 0; list.Delete(1, delElement); list.printList(); cout<<"刪除元素后數組鏈表的長度為"<<list.length()<<" " <<"刪除的元素是"<<" "<<delElement<<endl; return 0; } 輸出結果: 1 2 3 數組鏈表的長度為3 1 3 刪除元素后數組鏈表的長度為2 刪除的元素是 22、數組鏈表總結
(1)數組鏈表 鏈表中的每個元素都是數組,即由數組構成的鏈表(2)鏈表數組 數組中的每個元素都是鏈表,即由鏈表構成的數組
(3)二維鏈表 鏈表中的每個元素都是鏈表,即由鏈表構成的鏈表
總結
以上是生活随笔為你收集整理的数据结构与算法 -- 链表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL ALTER命令
- 下一篇: 第一部分 Calendar介绍