單鏈表,用於存儲邏輯關系為 "一對一" 的數據,與順序表不同,鏈表不限制數據的物理存儲狀態,換句話說,使用鏈表存儲的數據元素,其物理存儲位置是隨機的。
結點在存儲器中的位置是任意的,即邏輯上相鄰的數據元素在物理上不一定相鄰。
例如{1,2,3}:
線性表的鏈式表示又稱為非順序映像或鏈式映像。
各結點由兩個域組成:
數據域:存儲元素數值數據
指針域:存儲直接后繼結點的存儲位置
頭指針是指向鏈表中第一個結點的指針
首元結點是指鏈表中存儲第一個數據元素a1的結點
頭結點是在鏈表的首元結點之前附設的一個結點;數據域內只放空表標志和表長等信息
如何表示空表?
答:有頭結點時,當頭結點的指針域為空時表示空表
在鏈表中設置頭結點有什么好處?
答:⒈便於首元結點的處理
首元結點的地址保存在頭結點的指針域中,所以在鏈表的第一個位置上的操作和其它位置一致,無須進行特殊處理;
⒉便於空表和非空表的統一處理
無論鏈表是否為空,頭指針都是指向頭結點的非空指針,因此空表和非空表的處理也就統一了。
頭結點的數據域內裝的是什么?
答:頭結點的數據域可以為空,也可存放線性表長度等附加信息,但此結點不能計入鏈表長度值。
鏈表(鏈式存儲結構)的特點:
1)結點在存儲器中的位置是任意的,即邏輯上相鄰的數據元素在物理上不一定相鄰
2)訪問時只能通過頭指針進入鏈表,並通過每個結點的指針域向后掃描其余結點,所以尋找第一個結點和最后一個結點所花費的時間不等
這種存取元素的方法被稱為順序存取法
鏈表的優缺點:
優點:數據元素的個數可以自由擴充
插入、刪除等操作不必移動數據,只需修改鏈接指針,修改效率較高鏈表的優缺點
缺點:存儲密度小
存取效率不高,必須采用順序存取,即存取數據元素時,只能按鏈表的順序進行訪問(順藤摸瓜)
單鏈表是由表頭唯一確定,因此單鏈表可以用頭指針的名字來命名
若頭指針名是L,則把鏈表稱為表L
用c++描述的單鏈表大致可以分兩種,一種用結構體定義,一種用類定義;
鏈表也可以分為頭插法和尾插法
頭插法
生成新結點
將讀入數據存放到新結點的數據域中
將該新結點插入到鏈表的前端
1 #include<iostream> 2 using namespace std; 3 4 typedef struct SingLinkList { 5 int data; //數據域 6 struct SingLinkList *next; //指針域 7 }SLL,*Slist; //相當於struct SingLinkList,*Slist相當於struct SingLinkList * 8 9 //新建鏈表,確認鏈表的長度 10 void New_LinkList(Slist L,int n) //Slist L相當於struct SingLinkList * L 11 { 12 int i; 13 int v; 14 Slist p; 15 L->next = NULL; 16 17 //這是用前插法創建鏈表,此時就不需要使用尾結點了 18 //所以遍歷的結果是與輸入值的順序是相反的 19 for (int i = n; i > 0; i--) 20 { 21 p = (Slist)malloc(sizeof(SLL)); 22 cin >> v; 23 p->data = v; 24 p->next = L->next; //p的指針域指向L的指針域,即p指向空, 25 L->next = p; //但是此時只有p這個結點,還需要頭結點指向p 26 //此時頭結點指向p,而p指針域指向最后, 27 //可以認為p是尾結點,它的指針域自然要指向空了。 28 //然后你需要幾個結點就繼續按照原來的一直干下去。記住這是前插法。 29 //單鏈表的結點不像數組可以通過下標找到,它得從頭開始找。 30 //每個節點都與上一個結點相連沒有名字是地址傳地址 31 } 32 } 33 34 // 獲取鏈表中指定位置的數據元素 35 int GetElem_LinkList( Slist L, int i, int *e) 36 { 37 Slist p; 38 p = L->next; 39 int j = 1; 40 41 while (p && j < i) 42 { 43 p = p->next; 44 ++j; 45 } 46 47 if (!p || j > i) 48 { 49 return 0; 50 } 51 52 *e = p->data; 53 return 1; 54 } 55 56 // 在鏈表中指定位置插入數據 57 int Isert_LinkList(Slist L, int i, int* e) 58 { 59 Slist p, s; 60 p = L; 61 int j = 0; 62 63 while (p && j < i - 1) 64 { 65 p = p->next; 66 ++j; 67 } 68 69 if (!p || j > i - 1) 70 { 71 return 0; 72 } 73 74 s = (Slist)malloc(sizeof(SLL)); 75 s->data = *e; 76 s->next = p->next; 77 p->next = s; 78 79 return 1; 80 } 81 82 // 刪除鏈表中指定數據元素 83 int Delete_LinkList(Slist L, int i, int* e) 84 { 85 Slist p, q; 86 p = L; 87 int j = 0; 88 89 while (p->next && j < i - 1) 90 { 91 p = p->next; 92 ++j; 93 } 94 95 if (!(p->next) || j > i - 1) 96 { 97 return 0; 98 } 99 100 101 q = p->next; 102 p->next = q->next; 103 *e = q->data; 104 105 free(q); 106 return 1; 107 } 108 109 // 顯示鏈表元素的內容 110 int Print_LinkList(Slist L, char* s) 111 { 112 Slist p; 113 114 p = L->next; 115 if (p == NULL) 116 { 117 cout << "該鏈表為空!" << endl; 118 return 0; 119 } 120 121 while (p != NULL) 122 { 123 cout << p->data << " "; 124 p = p->next; 125 } 126 cout << endl; 127 return 1; 128 } 129 130 int main() 131 { 132 // 創建並初始化鏈表 133 SLL L; 134 cout << "請輸入五個節點的數據" << endl; 135 New_LinkList(&L, 5); 136 Print_LinkList(&L, (char*)"初始化鏈表:"); 137 138 int s, v; 139 140 // 插入數據 141 cout << "請輸入數據插入的位置和值:"; 142 cin >> s >> v; 143 Isert_LinkList(&L, s, &v); 144 Print_LinkList(&L, (char*)"插入后的節點數:"); 145 146 147 // 查詢數據 148 cout << "請輸入數據查詢的位置:"; 149 cin >> s; 150 GetElem_LinkList(&L, s, &v); 151 cout << "第" << s << "個數據是:" << v << endl; 152 153 // 刪除出數據 154 cout << "請輸入數據刪除的位置:"; 155 cin >> s; 156 if (Delete_LinkList(&L, s, &v)) 157 { 158 cout << "數據刪除成功,你所刪除的數據是:" << v << endl; 159 } 160 else 161 { 162 cout << "數據刪除失敗!" << endl; 163 } 164 Print_LinkList(&L, (char*)"刪除后的鏈表:"); 165 166 167 return 0; 168 }
尾插法
從一個空表L開始,將新結點逐個插入到鏈表的尾部,尾指針r指向鏈表的尾結點。
初始時,r同L均指向頭結點。每讀入一個數據元素則申請一個新結點,將新結點插入到尾結點后,r指向新結點。
1 #include <iostream> 2 using namespace std; 3 4 #define ERROR 0 5 #define OK 1 6 7 8 // 鏈表節點的結構體 9 typedef struct LNode 10 { 11 int data; // 數據域 12 struct LNode* next; // 指針域 13 }SLL , * SLNode; 14 15 16 // 新建鏈表,確認鏈表的長度 17 void New_LinkList(SLNode L, int n) 18 { 19 int i; 20 int v; 21 22 SLNode p,r; // 新建單鏈表 23 L->next = NULL; 24 r = L; //尾指針r指向頭節點 25 26 //鏈表的尾插法 27 for (int i = n; i > 0; i--) 28 { 29 p = (SLNode)malloc(sizeof(SLL)); //生成新結點 30 cin >> v; 31 p->data = v; //輸入元素值 32 p->next = NULL; r->next = p; //插入到表尾 33 r = p; //r指向新的尾結點 34 //插入的結點p是尾結點,所以要使r指向p,才能讓再次插入的 35 //結點順利執行第31行。 36 } 37 } 38 39 40 // 獲取鏈表中指定位置的數據元素 41 int GetElem_LinkList(SLNode L, int i, int *e) 42 { 43 SLNode p; 44 p = L->next; 45 int j = 1; 46 47 while (p && j < i) 48 { 49 p = p->next; 50 ++j; 51 } 52 53 if (!p || j > i) 54 { 55 return ERROR; 56 } 57 58 *e = p->data; 59 return OK; 60 } 61 62 63 // 在鏈表中指定位置插入數據 64 int Isert_LinkList(SLNode L, int i, int* e) 65 { 66 SLNode p, s; 67 p = L; 68 int j = 0; 69 70 while (p && j < i - 1) 71 { 72 p = p->next; 73 ++j; 74 } 75 76 if (!p || j > i - 1) 77 { 78 return ERROR; 79 } 80 81 s = (SLNode)malloc(sizeof(SLL)); 82 s->data = *e; 83 s->next = p->next; 84 p->next = s; 85 86 return OK; 87 } 88 89 90 // 刪除鏈表中制定數據元素 91 int Delete_LinkList(SLNode L, int i, int* e) 92 { 93 SLNode p, q; 94 p = L; 95 int j = 0; 96 97 while (p->next && j < i - 1) 98 { 99 p = p->next; 100 ++j; 101 } 102 103 if (!(p->next) || j > i - 1) 104 { 105 return ERROR; 106 } 107 108 109 q = p->next; 110 p->next = q->next; 111 *e = q->data; 112 113 free(q); 114 return OK; 115 } 116 117 118 // 顯示鏈表元素的內容 119 int Print_LinkList(SLNode L, char* s) 120 { 121 SLNode p; 122 123 p = L->next; 124 if (p == NULL) 125 { 126 cout << "該鏈表為空!" << endl; 127 return ERROR; 128 } 129 130 while (p != NULL) 131 { 132 cout << p->data << " "; 133 p = p->next; 134 } 135 cout << endl; 136 return OK; 137 } 138 139 140 int main() 141 { 142 // 創建並初始化鏈表 143 struct LNode L; 144 cout << "請輸入五個節點的數據" << endl; 145 New_LinkList(&L, 5); 146 Print_LinkList(&L, (char*)"初始化鏈表:"); 147 148 int s, v; 149 150 // 插入數據 151 cout << "請輸入數據插入的位置和值:"; 152 cin >> s >> v; 153 Isert_LinkList(&L, s, &v); 154 Print_LinkList(&L, (char*)"插入后的節點數:"); 155 156 157 // 查詢數據 158 cout << "請輸入數據查詢的位置:"; 159 cin >> s; 160 GetElem_LinkList(&L, s, &v); 161 cout << "第" << s << "個數據是:" << v << endl; 162 163 // 刪除出數據 164 cout << "請輸入數據刪除的位置:"; 165 cin >> s; 166 if (Delete_LinkList(&L, s, &v)) 167 { 168 cout << "數據刪除成功,你所刪除的數據是:" << v << endl; 169 } 170 else 171 { 172 cout << "數據刪除失敗!" << endl; 173 } 174 Print_LinkList(&L, (char*)"刪除后的鏈表:"); 175 176 177 return 0; 178 }