前言
有急事回了家一趟,進度被拖了很久。回來接着看數據結構,發現順序表和單鏈表差別還是很大的,由於單鏈表涉及到較多指針的操作,光靠註釋比較難說清楚,不能直接上代碼了,所以花了不少時間來畫圖。單鏈表的一些特性,鮑老師講得很細,我也是跟着他的思路走,把單鏈表分成兩個部分來講,第一個部分主要是講單鏈表最基礎的創建和原理,第二個部分則是增加了單鏈表的管理結構,便於更好地管理單鏈表。
順序表與單鏈表
數據結構分爲邏輯結構、物理(存儲)結構、數據運算三個部分,邏輯結構指的是數據之間的關係,可分爲線性結構和非線性結構兩類;物理結構指的是數據的邏輯結構在計算機存儲空間內的存放方式,包括順序結構、鏈式結構、索引結構和散列結構。一種邏輯結構在計算機裏可以用不同的存儲結構實現。
線性表是一種常見的邏輯結構,多用順序存儲(稱爲順序表)或鏈式存儲(稱爲單鏈表)來實現。
順序存儲結構中,存儲單元的地址必須是連續的,所以邏輯上相鄰的元素在物理上也必然相鄰。因此在初始化順序表時,需要開闢好一整塊連續的內存空間以供使用;當預先設置的內存空間不足時,需要去申請另外一整塊內存來使用,以達到動態分配內存的效果。
而鏈式存儲結構不要求物理地址是否連續,使用指針來實現元素之間的邏輯關係,所以鏈式結構除了設計一個數據域來存儲數據本身外,還需要一個指針域來存儲其後繼元素的地址。鏈式存儲不需要一整塊的內存空間,可以有效利用碎片化的內存,在每次需要時去纔去申請空間,達到真正的動態分配內存的效果。
由於順序存儲和鏈式存儲的特性,它們各有着一些優缺點,比如順序存儲可以按元素序號直接訪問,因此在讀取數據時效率很快;而鏈式存儲則在做插入或刪除操作時很方便。
頭結點與頭指針
- 首元結點:鏈表中存儲的第一個數據元素的結點。
- 頭結點:爲了處理方便,在首元結點之前附設的一個結點。有了頭結點,對首元結點進行插入或者刪除的操作就跟對其他結點的操作是一致的了,有利於操作的統一。
- 頭結點的數據域可以不存儲任何信息,也可以存儲於數據元素類型相同的附加信息。
- 頭指針:指向鏈表中第一個節點的指針。如果鏈表有頭結點,那麼頭指針所指向的結點就是鏈表的頭結點;如果鏈表不設頭結點,那麼頭指針所指向的結點就是鏈表的首元結點。
單鏈表的初始化
無頭結點:
注:
有頭結點:
單鏈表的插入
在有無頭結點這兩種情況下,對鏈表進行頭部插入、尾部插入的操作是有區別的
頭部插入
無頭結點:生成一個新結點,將新結點的next指向首元結點,再將頭指針指向新結點即可
有頭結點:生成一個新結點,將新結點的next指向首元結點,再將頭結點的next指向新結點即可
尾部插入
思路是一致的:生成一個新結點,遍歷整個鏈表到尾部,在尾部插入新結點即可
因此,設置頭結點有利於鏈表操作的統一,並且頭結點的數據域可以儲存鏈表的相關信息,方便對鏈表進行處理。
全部代碼
無頭結點
#include<stdio.h>
#include <assert.h>
#include <stdlib.h>
//保存的數據類型
#define ElemType int
typedef struct ListNode{
//數據域
ElemType data;
//指針域,指向下一個結構體的地址
struct ListNode *next;
}ListNode;
typedef ListNode* List;
//初始化單鏈表
void InitList(List *head);
//尾部插入
void push_back(List *head,ElemType x);
//頭部插入
void push_fount(List *head,ElemType x);
//展示單鏈表
void ShowList(List head);
int main(void){
List myList;
InitList(&myList);
int select = 1;
int input;
while (select) {
printf("***************************************************\n");
printf("* [1] push_back [2] push_front * \n");
printf("* [3] show *\n");
printf("***************************************************\n");
printf("請選擇:\n");
scanf("%d",&select);
if(select == 0){
break;
}
switch (select) {
case 1:
printf("請輸入:\n");
scanf("%d",&input);
push_back(&myList, input);
break;
case 2:
printf("請輸入:\n");
scanf("%d",&input);
push_fount(&myList, input);
break;
case 3:
ShowList(myList);
break;
default:
break;
}
}
return 0;
}
//初始化單鏈表
void InitList(List *head){
*head = NULL;
}
//尾部插入 無頭結點
void push_back(List *head,ElemType x){
//判斷是否已經有第一個結點
if (*head == NULL) {
//沒有,創建第一個結點並存值
*head = (ListNode *)malloc(sizeof(ListNode));
assert(*head != NULL);
(*head)->data = x;
(*head)->next = NULL;
}else{
//已經有第一個結點了,創建一個結點並存值
List s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
s->data = x;
s->next = NULL;
List p = (*head);
while (p->next != NULL) {
p = p->next;
}
p->next= s;
}
}
//頭部插入 無頭結點
void push_fount(List *head,ElemType x){
List s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
s->data = x;
s->next = (*head);
(*head) = s;
}
//展示單鏈表
void ShowList(List head){
ListNode *p = head;
while (p != NULL) {
printf("%4d",p->data);
p = p->next;
}
printf("\n");
}
有頭結點
#include<stdio.h>
#include <assert.h>
#include <malloc.h>
//保存的數據類型
#define ElemType int
typedef struct ListNode{
//數據域
ElemType data;
//指針域,指向下一個結構體的地址
struct ListNode *next;
}ListNode;
typedef ListNode* List;
//初始化單鏈表
void InitList(List *head);
//尾部插入
void push_back(List *head,ElemType x);
//頭部插入
void push_fount(List *head,ElemType x);
//展示單鏈表
void ShowList(List head);
int main(void){
List myList;
InitList(&myList);
int select = 1;
int input;
while (select) {
printf("***************************************************\n");
printf("* [1] push_back [2] push_front * \n");
printf("* [3] show *\n");
printf("***************************************************\n");
printf("請選擇:\n");
scanf("%d",&select);
if(select == 0){
break;
}
switch (select) {
case 1:
printf("請輸入:\n");
scanf("%d",&input);
push_back(&myList, input);
break;
case 2:
printf("請輸入:\n");
scanf("%d",&input);
push_fount(&myList, input);
break;
case 3:
ShowList(myList);
break;
default:
break;
}
}
return 0;
}
//初始化單鏈表
void InitList(List *head){
*head = (ListNode *)malloc(sizeof(ListNode));
assert(*head != NULL);
(*head)->next = NULL;
}
//尾部插入 有頭結點
void push_back(List *head,ElemType x){
//創建一個結點並存值
List s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
s->data = x;
s->next = NULL;
List p = (*head);
while (p->next != NULL) {
p = p->next;
//printf("here\n");
}
p->next = s;
}
//頭部插入 有頭結點
void push_fount(List *head,ElemType x){
List s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
s->data = x;
s->next = (*head)->next;
(*head)->next = s;
}
//展示單鏈表
void ShowList(List head){
ListNode *p = head->next;
while (p != NULL) {
printf("%4d",p->data);
p = p->next;
}
printf("\n");
}