連結串列的多種實現方式
阿新 • • 發佈:2018-12-08
連結串列是什麼???
連結串列是一種區別於順序儲存的非連續、非順序的物理儲存結構,也就是說,不像佇列、堆疊之類的邏輯資料結構,它只是一種資料儲存的物理結構。我們所使用的陣列便是一種物理儲存結構。
但與陣列不同的是,它是鏈式儲存的,而陣列採用了順序儲存。
那麼,什麼是順序儲存與鏈式儲存呢(⊙o⊙)?
順序儲存:把邏輯上相鄰的結點儲存在物理位置上相鄰的儲存單元,即每個結點之間的地址是連續的。
鏈式儲存:用一組任意的儲存單元儲存線性表的陣列元素,結點之間的地址不一定是連續的,但每個結點之間一定有著某種先後關係。
我們使用鏈式儲存,可以更方便地插入、刪除、更新元素,但是頻繁查詢元素時較耗時間。同時,連結串列由於多了指標域,更耗空間,卻又避免了陣列的大幅空間浪費的情況。
連結串列程式碼之指標實現:
//連結串列(包括有關連結串列的各種操作) #include<cstdio> #include<cstdlib> using namespace std; typedef struct List *Link;//Link代表連結串列指標 typedef struct List Lnode;//Lnode代表連結串列結點 struct List { int data; Link next; }; Link create(Link Head)//建表操作並返回頭指標View Code{ int newdata; Link newnode; Head=(Link)malloc(sizeof(Lnode)); printf("Please input number:\n"); scanf("%d",&newdata); Head->data=newdata; Head->next=NULL; while(1) { newnode=(Link)malloc(sizeof(Lnode)); if(newnode==NULL)//若空間分配失敗 ,則終止迴圈{ printf("Sorry, it failed!"); break; } printf("Please input number : intput '-1' means exit\n");//輸入連結串列元素,輸入-1代表輸入結束 scanf("%d",&newdata); if(newdata==-1) return Head; newnode->data=newdata; newnode->next=Head; Head=newnode; } return Head; } void dispaly(Link Head)//連結串列元素的顯示 { Link p=Head; if(p==NULL)//連結串列為空 printf("\nList is empty\n"); else do { printf("%d",p->data); p=p->next; }while(p!=NULL); return; } Link insert(Link Head, int value, int i)//Head為頭指標,value為插入值,i為插入結點的編號 { Link newnode,p=Head; int newdata,j=1; newnode=(Link)malloc(sizeof(Lnode)); newnode->data=value; if(i==1) { newnode->next=Head; Head=newnode; } else { while(j<i-1&&p->next!=NULL)//找到 i 結點的前一個元素 { p=p->next; j++; } if(j==i-1) { newnode->next=p->next; p->next=newnode; } else printf("Insert is Failure,i is not right!\n"); } return Head; } Link del(Link Head, int i)//Head為頭指標,i為刪除結點編號 { Link p=Head,t; int j=1; if(i==1) { Head=Head->next; } else { while(j<i-1&&p->next!=NULL) { p=p->next; j++; } if(j==i-1&&p->next!=NULL) { t=p->next; p->next=t->next; } if(t!=NULL) free(t);//釋放記憶體,儘可能地節省空間 } return Head; } int get(Link Head, int i)//取編號為i的結點的值 { int j=1; Link p; p=Head; while(j<i&&p!=NULL) { p=p->next; j++; } if(p!=NULL) return p->data; else printf("Data is error!\n"); return -1; } int locate(Link Head, int x)//查詢第一個值為x的結點的編號,若失敗,則返回-1 { int n=1; Link p=Head; while(p!=NULL&&p->data!=x) { p=p->next; n++; } if(p!=NULL) return n; else printf("Not found!\n"); return -1; } int lengh(Link Head)//取連結串列長度 { int len=0; Link p=Head; while(p!=NULL) { len++; p=p->next; } return len; } Link connect(Link Head1, Link Head2)//以前面的連結串列為頭,連線後續連結串列 { Link p=Head1; while(p->next!=NULL) { p=p->next; } p->next=Head2; return Head1; } bool compare(Link Head1, Link Head2)//比較兩個連結串列是否相同 { Link p1,p2; p1=Head1; p2=Head2; while(1) { if(p1->next==NULL&&p2->next==NULL) return 1; else if(p1->data!=p2->data) return 0; else { p1=p1->next; p2=p2->next; } } } Link Set_NULL(Link Head)//清空連結串列 { Link p; p=Head; while(p!=NULL) { p=p->next; free(Head); Head=p; } return Head; } int main() { //連結串列的相關操作已給出 //以下省略一千字...... }
但實際上,我們同樣可以用陣列來模擬連結串列,而且對其操作的速度將大大提高(我也是在一位神犇那兒知道的),因為CPU快取對連續的資料結構速度更快(貌似是因為儲存結構層次之類亂七八糟的東西)!
因此,下面給出陣列模擬連結串列的程式碼。
連結串列程式碼之陣列實現:
//陣列模擬連結串列的演示程式 #include<iostream> #include<cstdlib> #define MAXN 100001//連結串列中最多能容納的元素數+1 using namespace std; int linklst_data[MAXN] ;//linklst_data 為連結串列中元素的資料 int linklst_point[MAXN] ;//linklst_point 為連結串列中元素的指標 int head = -1;/*連結串列的頭指標*/ void del_by_data(int del_data)//刪除一個含有del_data的元素 { int p=head,pre=-1;//從頭開始遍歷連結串列 while(p != -1) { if(linklst_data[p] == del_data)//如找到了要刪除的值,則進行刪除操作 { if(p == head)//如果刪除的是頭指標,則更新頭指標 head = linklst_point[head]; else//這個else 等同於 if (pre != 1)因為當p!=head時p前面已經有元素了 linklst_point[pre] = linklst_point[p]; linklst_data[p] = -1;//刪除p指向的元素的值*/ linklst_point[p] = -1; return ;/*結束刪除操作*/ } pre = p; p = linklst_point[p]; } } void add_front(int add_data)//在連結串列前段加入元素 { int p = 1; while(linklst_data[p] != -1 && p < MAXN) //找一空位儲存資料,這裡可以優化 ++p; linklst_data[p] = add_data; //將要加入的節點選好的空位賦值 linklst_point[p] = head; //將當前加入元素的指標指向head head = p; //使當前的元素成為連結串列頭指標 } void add_rear(int add_data)//在連結串列末尾加入元素 { int p = 1,pre; while(linklst_data[p] != -1 && p < MAXN )//找到空位 ++p; linklst_data[p] = add_data; //該空位賦值 if( head != -1 ) //連結串列不為空 { pre = head;//找到連結串列中的最後一個元素*/ while(linklst_point[pre] != -1)//找到空指標 pre = linklst_point[pre]; linklst_point[pre] = p;//將當前連結串列中最後一個元素的指標指向要加入的元素 } else//否則直接對head所指的元素賦值 head = p; return ; } void output() { int p=head; cout<<"list is: "; while(p != -1) { cout<<linklst_data[p]<<" "; p = linklst_point[p]; } cout<<"\n"; } void init()//初始化陣列指標值 { for(int i = 0;i < MAXN;i++)//連結串列中的空值設定為-1 { linklst_point[i] = -1; linklst_data[i] = -1; } } int main()//演示連結串列的操作 { int ins,data; init(); while(1) { cout<<"1.insert a value in front \n"; cout<<"2.insert a value in rear \n"; cout<<"3.delete a value \n"; cout<<"4.quit \n"; cin>>ins; switch(ins) { case 1: cout<<"please insert a value :"; cin>>data; add_front(data); break; case 2: cout<<"please insert a value :"; cin>>data; add_rear(data); break; case 3: cout<<"please insert a value :"; cin>>data; del_by_data(data); break; default: return 0; } system("cls");//清空螢幕,此命令只能在window平臺下使用 output(); } return 0; }View Code
從程式碼中可以發現,每次進行插入操作時,都要在陣列中找一空位進行操作,這就導致每次插入操作的時間複雜度都是線性的,導致很大的時間浪費。
因此,我們可以通過堆疊記錄沒有被加入連結串列的元素的下標,每次插入操作只需從棧中獲取下標即可。這樣,O(n)的線性查詢便轉化為了O(1)的出棧操作,時間上大大優化。
連結串列程式碼之陣列實現(堆疊優化):
//陣列連結串列的演示程式(棧優化) #include<iostream> #include<cstdlib> #define MAXN 100001 using namespace std; int linklst_data[MAXN] ; int linklst_point[MAXN] ; int stack[MAXN];//用於記錄沒有加入連結串列的陣列下標的棧 int head = -1;//連結串列的頭指標 int stack_head = 0;//棧頂指標 void del_by_data(int del_data)//刪除一個含有del_data的元素 { int p=head,pre=-1; //從頭開始遍歷連結串列 while(p != -1) { if(linklst_data[p] == del_data)//找到要刪除的值,進行刪除操作 { if(p == head)//如果刪除的是頭指標,則更新頭指標 head = linklst_point[head]; else//這個else 等同於 if (pre != 1)因為當p!=head時p前面已經有元素了 linklst_point[pre] = linklst_point[p]; linklst_data[p] = -1;//刪除p指向的元素的值 linklst_point[p] = -1; stack[--stack_head] = p;//將刪除元素的陣列下標入棧 return ; } pre = p; p = linklst_point[p]; } return ; } void add_front(int add_data)//在連結串列前段加入元素*/ { int p = 1; p = stack[stack_head++];//直接取出棧中的空位*/ linklst_data[p] = add_data;//將要加入的節點選好的空位賦值*/ linklst_point[p] = head; //將當前加入元素的指標指向head*/ head = p; //使當前的元素成為連結串列頭指標*/ } void add_rear(int add_data)//在連結串列末尾加入元素*/ { int p = 1,pre; p = stack[stack_head++];//直接取出棧中的空位*/ linklst_data[p] = add_data; //對要加入的節點的空位進行賦值*/ if( head != -1 ) //連結串列不為空 { pre = head;//找到連結串列中的最後一個元素*/ while(linklst_point[pre] != -1) pre = linklst_point[pre]; linklst_point[pre] = p;//將當前連結串列中最後一個元素的指標指向要加入的元素 } else//否則直接對head所指的元素賦值 head = p; } void output() { int p=head; cout<<"List: "; while(p != -1) { cout<<linklst_data[p]<<" "; p = linklst_point[p]; } cout<<"\n"; return ; } void init()//初始化陣列指標值 { for(int i = 0;i < MAXN;i++) { linklst_point[i] = -1; linklst_data[i] = -1; stack[i] = i + 1;//從1到MAXN給棧加入陣列下標 } return ; } int main()//演示連結串列的操作 { int ins,data; init(); while(1) { cout<<"1.insert a value in front \n"; cout<<"2.insert a value in rear \n"; cout<<"3.delete a value \n"; cout<<"4.quit \n"; cin>>ins; switch(ins) { case 1: cout<<"please insert a value :"; cin>>data; add_front(data); break; case 2: cout<<"please insert a value :"; cin>>data; add_rear(data); break; case 3: cout<<"please insert a value :"; cin>>data; del_by_data(data); break; default: return 0; } system("cls");//此命令只能在window平臺下使用 output(); } return 0; }View Code
以上便是我所學習的第一個資料結構--連結串列了,若有不足,請指正!!!