數據結構(一)線性表鏈式存儲實現
阿新 • • 發佈:2018-08-04
spl 原因 pause main -- 基本 無法 輸入 pen
(一)前提
在前面的線性表順序存儲結構,最大的缺點是插入和刪除需要移動大量的元素,需要耗費較多的時間。
原因:在相鄰兩個元素的存儲位置也具有鄰居關系,他們在內存中的位置是緊挨著的,中間沒有間隙,當然無法快速插入和刪除。
為了解決這個為題,出現了鏈式存儲結構
(二)鏈式線性表兩種結構(帶有頭結點和不帶頭結點)
不帶頭結點:
空鏈表:
帶有頭結點:
空鏈表:
(三)頭結點和頭指針的區別
頭指針:
1.頭指針是指向第一個結點的指針,若是鏈表中只有頭結點,則是指向頭結點的指針 2.頭指針具有標識作用,所以常常以頭指針冠以鏈表的名字(指針變量名) 3.無論鏈表是否為空,頭結點均不為空 4.頭指針是鏈表必要元素
頭結點:
1.頭結點是為了操作統一和方便而設立的,放在第一個元素結點之前,其數據域一般無意義(也可以存放表長度) 2.有了頭結點,對第一個元素結點的插入,刪除,其操作和其他結點操作統一了 3.頭結點不一定是鏈表的必要元素
(四)帶頭結點的單鏈表實現
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType; typedefint Status; typedef struct Node { ElemType data; struct Node* next; }Node; typedef struct Node* LinkList; //四個基本操作,初始,清空,判斷是否為空,獲取長度 Status InitList(LinkList* L); Status ClearList(LinkList* L); Status ListEmpty(LinkList L); int ListLength(LinkList L); //四個元素操作,插入,刪除,兩種查找 Status GetElem(LinkList L, inti, ElemType* e); int LocateElem(LinkList L, ElemType e); Status ListInsert(LinkList* L, int i, ElemType e); Status ListDelete(LinkList* L, int i, ElemType* e); //用來打印鏈表 void PrintList(LinkList L); int main() { LinkList L; ElemType e; Status ret; int i; printf("1.InitList\n"); InitList(&L); printf("2.1 Insert range of 5 elements by head\n"); for (i = 1; i <= 5;i++) { ListInsert(&L, 1, i); } printf("2.2 Insert range of 5 elements by end\n"); for (; i <= 10; i++) { ListInsert(&L, ListLength(L)+1, i); } PrintList(L); printf("3. list length:%d\n", ListLength(L)); printf("4.find element by element:%d get index:%d\n",8, LocateElem(L, 8)); GetElem(L, 6, &e); printf("5.find element by index:%d get element:%d\n",6,e); ListDelete(&L, 3, &e); printf("6.delete element by index:%d get element:%d\n",3,e); PrintList(L); printf("7.Clear\n"); ClearList(&L); printf("8.is empty:%d\n", ListEmpty(L)); system("pause"); return 0; } //四個基本操作,初始,清空,判斷是否為空,獲取長度 //初始化帶有頭結點的鏈表 Status InitList(LinkList* L) { *L = (LinkList)malloc(sizeof(Node)); //使頭指針指向頭結點 if (*L == NULL) //內存分配失敗 return ERROR; (*L)->next = NULL; //指針域為空 (*L)->data = 0; //頭結點數據域用來存放鏈表長度 return OK; } //清空鏈表(不會清除頭結點) Status ClearList(LinkList* L) { LinkList q, p; q = (*L)->next; //是q指向第一個結點 while (q) { p = q; q = q->next; free(p); } (*L)->next = NULL; return OK; } //判斷鏈表是否為空 Status ListEmpty(LinkList L) { if (L->next) return FALSE; return TRUE; } //獲取列表長度 int ListLength(LinkList L) { /* int length=0; LinkList q=L; while (q=q->next) length++; return length; */ return L->data; } //四個元素操作,插入,刪除,兩種查找 //按照索引查找,獲取元素 Status GetElem(LinkList L, int i, ElemType* e) { int j=1; LinkList q = L->next; while (q&&j<i) { q = q->next; j++; } if (!q || j>i) //對結果進行判斷!q是沒有找到數據,已經到達尾結點,j>i是針對函數輸入進行判斷,例如輸入i=0,就不會走上面循環但是p!=null,這時節點也沒有找到,就需要我們進行判斷,針對這個,我們也可以在函數開始就進行長度校驗,更加容易理解 return ERROR; *e = q->data; return OK; } //按照元素進行查找,獲取索引 int LocateElem(LinkList L, ElemType e) { int j=0; LinkList q = L->next; while (q) { j++; if (q->data == e) break; q = q->next; } return j; } //按照索引進行插入數據 Status ListInsert(LinkList* L, int i, ElemType e) { if (L == NULL && i > (*L)->data+1) return ERROR; int j = 1; LinkList q = *L; while (q&&j<i) { q = q->next; j++; } if (i < j) return ERROR; //新建節點,加入鏈表 LinkList n = (LinkList)malloc(sizeof(Node)); n->data = e; n->next = q->next; q->next = n; (*L)->data++; //長度自加 return OK; } //進行元素刪除 Status ListDelete(LinkList* L, int i, ElemType* e) { if (L == NULL || i>(*L)->data) return ERROR; int j=1; LinkList q, p; q = *L; while (q->next&&j<i) //這是去找指定索引元素的直接前驅 { q = q->next; j++; } if (!(q->next) || j>i) return ERROR; p = q->next; //這才是我們要刪除的 *e = p->data; q->next = p->next; free(p); (*L)->data--; return OK; } //用來打印鏈表 void PrintList(LinkList L) { printf("begin print data:\n"); LinkList q = L->next; while (q) { printf("%d ", q->data); q = q->next; } printf("\nend print data\n"); }
1.InitList 2.1 Insert range of 5 elements by head 2.2 Insert range of 5 elements by end begin print data: 5 4 3 2 1 6 7 8 9 10 end print data 3. list length:10 4.find element by element:8 get index:8 5.find element by index:6 get element:6 6.delete element by index:3 get element:3 begin print data: 5 4 2 1 6 7 8 9 10 end print data 7.Clear 8.is empty:1 請按任意鍵繼續. . .輸出結果
註意:對於元素的插入和刪除註意索引的正確與否
補充:使用頭插法和尾插法創建單鏈表
//頭插法和尾插法創建單鏈表 void CreateListHead(LinkList* L, int n); void CreateListEnd(LinkList* L, int n); //頭插法 void CreateListHead(LinkList* L, int n) { LinkList q; //創建頭結點 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; (*L)->data = 0; srand(time(0)); for (int i = 0; i < n;i++) { q = (LinkList)malloc(sizeof(Node)); //創建一個新節點 q->data = rand() % 100 + 1; q->next = (*L)->next; (*L)->next = q; (*L)->data++; } } //尾插法 void CreateListEnd(LinkList* L, int n) { LinkList q, p; //創建頭結點 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; (*L)->data = 0; p = *L; srand(time(0)); //創建隨機種子 for (int i = 0; i < n; i++) { q = (LinkList)malloc(sizeof(Node)); //創建一個新節點 q->data = rand() % 10 + 1; //進行指針交換 q->next = p->next; //這一步可以省去,但是需要在for循環後面加上p->next=NULL; p->next = q; //將p指針始終指向最後元素 p = q; //頭結點數據域自增記錄鏈表長度 (*L)->data++; } }
(五)時間復雜度的分析
單鏈表的查找性能為O(n)---->順序存儲結構為O(1) 單鏈表的插入和刪除,在計算出某位置的指針後,插入和刪除性能為O(1)----->順序存儲結構為O(n)
(六)空間性能分析
順序存儲結構需要預分配存儲空間。
單鏈表不需要分配存儲空間,元素限制不受控制
(七)總結
若線性表需要頻繁查找,很少進行插入和刪除操作,應該選擇順序存儲結構。
當線性表中元素個數變化較大或者根本不知道有多大時,最好使用單鏈表結構,不需要考慮存儲空間大小問題
數據結構(一)線性表鏈式存儲實現