1. 程式人生 > 其它 >基礎---單鏈表的實現(帶頭)

基礎---單鏈表的實現(帶頭)

基本術語

  • 結點:單鏈表是一種鏈式存取的資料結構,連結串列中的資料是以結點來表示的,每個結點的構成:元素(資料元素的映象) + 指標(指示後繼元素儲存位置),元素就是儲存資料的儲存單元,指標就是連線每個結點的地址資料。

單鏈表的實現(帶頭連結串列)

定義一個結點
//使用結構體來定義一個結點。注: 只要不是關鍵字,那就都是變數名,不必因為看著太規範而懷疑它為關鍵字。
typedef struct Node{
    int data;//儲存資料,DATA部分。
    struct Node *next;//儲存指標,NEXT部分。
}Node,*LinkedList;//Node是結構體struct Node的別名,*LinkedList是一個指向struct Node的指標。
初始化結點
LinkedList listinit(){
    Node *L;
    L=(Node*)malloc(sizeof(Node));      //開闢空間 
    if(L==NULL){                     //判斷是否開闢空間失敗,這一步很有必要
        printf("申請空間失敗");
        //exit(0);                  //開闢空間失敗可以考慮直接結束程式
    }
    L->next=NULL;       //指標指向空
}
//檢查很有必要,但下面那幾個建立單鏈表的似乎都沒檢查。
//(沒試過)下面那幾個初始結點的操作,似乎可以直接用這個函式代替掉。這個函式裡面寫好檢查開闢成功與否的方法,再初始化結點時更加方便也更加安全。
建立單鏈表(重點)

分為兩種建立方式:(好像還有有頭結點的和沒頭結點的,暫時沒看)

//頭插入法建立單鏈表
LinkedList LinkedListCreatH() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申請頭結點空間
    L->next = NULL;                      //初始化一個空連結串列
  
    int x;                         //x為連結串列資料域中的資料
    while(scanf("%d",&x) != EOF) {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申請新的結點
        p->data = x;                     //結點資料域賦值
        p->next = L->next;     //將結點插入到表頭L-->|2|-->|1|-->NULL
        L->next = p;
    }
    return L;
/*
一步一步的分析:
1.聲明瞭一個指標變數L,這個指標變數儲存了一個結構體的地址。而這個結構體裡還可以有一個指標,這個指標(NEXT)不要和這個指標變數本身儲存的指標混淆。也就是L儲存了一個地址,L->next儲存了另外的地址,L的地址指向這個結構體,L->next的指標指向另外的結構體。
長話短說就是L是一個地址,*L才是一個結構體。
//第一輪迴圈
2.開闢了一個記憶體空間,目標結構體算是正式誕生了,這個結構體的next指向NULL。
3.聲明瞭一個指標變數p,開闢結構體記憶體空間。
4.*p的data存上資料x,*p的next存上*L的next
5.*L的next存上當前結構體p的地址(不要忘了p是一個指標變數,存的是*p的地址)
//第二輪迴圈
2.3.聲明瞭一個新的指標變數p,這個指標變數和前面p那個毫無關係。
4.新p的data存上資料,新p的next存上*L的next,
由第一輪迴圈我們可以知道此時的*L的next儲存的是第一輪那個*p的地址,這樣第二個p就順理成章地指向了第一個p。
5.*L的next存上當前結構體p的地址
//第三輪迴圈
。。。
*/

頭插法圖例:

//尾插法建立單鏈表
LinkedList LinkedListCreatT() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申請頭結點空間
    L->next = NULL;                  //初始化一個空連結串列
    Node *r;
    r = L;                          //r始終指向終端結點,開始時指向頭結點
    int x;                         //x為連結串列資料域中的資料
    while(scanf("%d",&x) != EOF) {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申請新的結點
        p->data = x;                     //結點資料域賦值
        r->next = p;            //將結點插入到表頭L-->|1|-->|2|-->NULL
        r = p;
    }
    r->next = NULL;
    return L;
}
/*
這些分析預設讀者理解了頭插法,所以採取簡寫的形式。
1.建立一個頭結點*L
2.建立一個容器r用於後面的移動。
//第一輪迴圈
3.建立結構體*p
4.存資料
5.看似是*r的next其實是*L的next,事實上兩者就是一個東西,因為上文把L指標所儲存的地址賦值給了r,L和r指向的是一個東西。
6.像步驟5一樣,讓r和p指向同一個東西---第一個*p
//第二輪迴圈
3.建立新結構體*p
4.存資料
5.看似是*r的next其實是步驟一中*p的next,事實上兩者就是一個東西,因為上文把第一個p指標所儲存的地址賦值給了r,第一個p和r指向的是一個東西。
6.像步驟5一樣,讓r和p指向同一個東西---第二個*p
//第三輪迴圈
3.建立新結構體*p
4.存資料
5.看似是*r的next其實是步驟二中*p的next,事實上兩者就是一個東西,因為上文把第二個p指標所儲存的地址賦值給了r,第二個p和r指向的是一個東西。
6.像步驟5一樣,讓r和p指向同一個東西---第三個*p
//第四輪迴圈
。。。
7.迴圈完畢後,很容易發現,最後一個*p的next是沒有值的,我們需要手動新增一個NULL給最後的*p
*/

尾插法圖例:

遍歷單鏈表
//遍歷輸出單鏈表
void printList(LinkedList L){
    Node *p=L->next;
    int i=0;
    while(p){
        printf("第%d個元素的值為:%d\n",++i,p->data);
        p=p->next;
    }
}
//p只有為NULL的時候,while迴圈才能被跳出。
//沒有破壞原連結串列,因為p是一個新定義的指標,沒有涉及到原連結串列的指標。
修改單鏈表
//連結串列內容的修改,再連結串列中修改值為x的元素變為為k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
    Node *p=L->next;
    int i=0;
    while(p){
        if(p->data==x){
            p->data=k;
        }
        p=p->next;
    }
    return L;
}
插入新結點
//單鏈表的插入,在連結串列的第i個位置插入x的元素
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
    Node *pre;                      //pre為前驅結點
    pre = L;
    int tempi = 0;
    for (tempi = 1; tempi < i; tempi++) {
        pre = pre->next;                 //查詢第i個位置的前驅結點
    }
    Node *p;                                //插入的結點為p
    p = (Node *)malloc(sizeof(Node));
    p->data = x;
    p->next = pre->next;         //將前置結點的next給新結點,使其指向原第i個結點。
    pre->next = p;               //使前置結點指向新結點。
  
    return L;
}
刪除結點
//單鏈表的刪除,在連結串列中刪除值為x的元素
  
LinkedList LinkedListDelete(LinkedList L,int x) {
    Node *p,*pre;                   //pre為前驅結點,p為查詢的結點。
    p = L->next;
     
    while(p->data != x) {              //查詢值為x的元素
        pre = p;					//
        p = p->next;				//這兩步操作完成後,pre始終落後p一個結點的身位。
    }
    pre->next = p->next;          //刪除操作,將其前驅next指向其後繼。
    free(p);
     
    return L;
}
//當迴圈到目標結點時,也就是while在退出迴圈的前一次迴圈中,pre通過迴圈的第一步指向目標刪除節點的前一個結點,p通過迴圈的第二步,指向了目標刪除節點。
//跳出迴圈後,讓前驅節點指向後繼結點,從而實現刪除的效果。
//這個被刪除的結點需要用free()函式來釋放記憶體空間。
完整的單鏈表程式碼
#include <stdio.h>
#include <stdlib.h>
 
//定義結點型別
typedef struct Node {
    int data;           //資料型別,你可以把int型的data換成任意資料型別,包括結構體struct等複合型別
    struct Node *next;          //單鏈表的指標域
} Node,*LinkedList;
 
//單鏈表的初始化
LinkedList LinkedListInit() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申請結點空間
    if(L==NULL){    //判斷申請空間是否失敗
        exit(0);    //如果失敗則退出程式
    }
    L->next = NULL;          //將next設定為NULL,初始長度為0的單鏈表
    return L;
}
 
 
//單鏈表的建立1,頭插法建立單鏈表
LinkedList LinkedListCreatH() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申請頭結點空間
    L->next = NULL;                      //初始化一個空連結串列
 
    int x;                         //x為連結串列資料域中的資料
    while(scanf("%d",&x) != EOF) {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申請新的結點
        p->data = x;                     //結點資料域賦值
        p->next = L->next;                    //將結點插入到表頭L-->|2|-->|1|-->NULL
        L->next = p;
    }
    return L;
}
 
 
//單鏈表的建立2,尾插法建立單鏈表
 
LinkedList LinkedListCreatT() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申請頭結點空間
    L->next = NULL;                  //初始化一個空連結串列
    Node *r;
    r = L;                          //r始終指向終端結點,開始時指向頭結點
    int x;                         //x為連結串列資料域中的資料
    while(scanf("%d",&x) != EOF) {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申請新的結點
        p->data = x;                     //結點資料域賦值
        r->next = p;                 //將結點插入到表頭L-->|1|-->|2|-->NULL
        r = p;
    }
    r->next = NULL;
 
    return L;
}
 
 
//單鏈表的插入,在連結串列的第i個位置插入x的元素
 
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
    Node *pre;                      //pre為前驅結點
    pre = L;
    int tempi = 0;
    for (tempi = 1; tempi < i; tempi++) {
        pre = pre->next;                 //查詢第i個位置的前驅結點
    }
    Node *p;                                //插入的結點為p
    p = (Node *)malloc(sizeof(Node));
    p->data = x;
    p->next = pre->next;
    pre->next = p;
 
    return L;
}
 
 
//單鏈表的刪除,在連結串列中刪除值為x的元素
 
LinkedList LinkedListDelete(LinkedList L,int x) {
    Node *p,*pre;                   //pre為前驅結點,p為查詢的結點。
    p = L->next;
     
    while(p->data != x) {              //查詢值為x的元素
        pre = p;
        p = p->next;
    }
    pre->next = p->next;          //刪除操作,將其前驅next指向其後繼。
    free(p);
     
    return L;
}
 
//連結串列內容的修改,再連結串列中修改值為x的元素變為為k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
    Node *p=L->next;
    int i=0;
    while(p){
        if(p->data==x){
            p->data=k;
        }
        p=p->next;
    }
    return L;
}
 
 
//便利輸出單鏈表
void printList(LinkedList L){
    Node *p=L->next;
    int i=0;
    while(p){
        printf("第%d個元素的值為:%d\n",++i,p->data);
        p=p->next;
    }
} 
 
int main() {
    //建立 
    LinkedList list;
    printf("請輸入單鏈表的資料:以EOF結尾\n");
    printf("以EOF結尾就是在末尾ctrl+z一下(windows系統下)\n");
    list = LinkedListCreatT();
    //list=LinkedListCreatT();
    printList(list);
     
    //插入 
    int i;
    int x;
    printf("請輸入插入資料的位置:");
    scanf("%d",&i);
    printf("請輸入插入資料的值:");
    scanf("%d",&x);
    LinkedListInsert(list,i,x);
    printList(list);
     
    //修改
    printf("請輸入修改的資料:");
    scanf("%d",&i);
    printf("請輸入修改後的值:");
    scanf("%d",&x);
    LinkedListReplace(list,i,x);
    printList(list);
     
    //刪除 
    printf("請輸入要刪除的元素的值:");
    scanf("%d",&x);
    LinkedListDelete(list,x);
    printList(list);
 
    return 0;
}