【數據結構】線性表之單鏈表

完整代碼需結合前面一篇順序表數據結構學習-線性表之順序表各種操作
網易云課堂小甲魚課程鏈接:數據結構與算法

線性表的鏈式存儲結構

線性表的順序存儲結構,最大的缺點就是插入和刪除時需要移動大量的元素,這顯然需要耗費時間。導致這個問題的原因是在于相鄰元素的存儲位置具有鄰居關系,它們在內存中的位置是緊挨著的,中間沒有間隙,當然無法快速插入和刪除。

定義

  • 線性表的鏈式存儲結構的特點是用一組任意的存儲單元存儲線性表中的數據元素,這組存儲單元可以存放在內存中未被占用的任意位置
  • 相比順序存儲結構,鏈式存儲結構中,除了需要存儲數據元素信息之外,還需要存儲它的后繼元素的存儲地址(指針)
  • 數據域:存儲數據元素信息的域,指針域:存儲直接后繼位置的域。指針域中存儲的信息成為指針或鏈。這兩部分信息組成數據元素成為存儲映像,成為結點(Node)。
    結點結構node
  • 鏈式結構:n個結點鏈接成一個鏈表,即為線性表(a1,a2,a3,...an);
  • 如果鏈表的每個結點中只包含一個指針域,那就叫做單鏈表

單鏈表

小甲魚視屏中的單鏈表
  • 頭指針:鏈表中的第一個結點的存儲位置。
  • 線性表中最后一個結點的指針域為空(NULL)。
  • 看圖說明:


    單鏈表的特點

頭結點和頭指針

  • 頭結點:

    • 頭結點是加在單鏈表之前附設的一個頭結點。
    • 頭結點的數據域一般不存儲任何信息,也可以存放一些關于線性表的長度的附加信息。
    • 頭結點的指針域存放指向第一個結點的指針(即第一個結點的存儲位置)。
    • 頭結點不一定是鏈表的必要元素。
  • 頭指針:

    • 頭指針是指鏈表指向第一個結點的指針,若鏈表有頭結點,則是指向頭結點的指針。
    • 頭指針具有標識作用,所以常用頭指針冠以鏈表的名字(指針變量的名字)。
    • 無論鏈表是否為空,頭指針均不為空。
    • 頭指針是鏈表的必要元素。


單鏈表的代碼實現

#include <stdio.h>
#define OK 1;
#define ERROR 0;
#define TRUE 1;
#define FALSE 0;
#define MAXSIZE 20  /* 定義線性表可能達到的最大長度 */
typedef int  ElemType; /*  定義數據元素類型,類型名為ElemType,此處所定義的數據元素只包含一個int型的數據項*/
typedef struct { /* 定義順序表類型,類型名為SqList,包含兩個數據項:數組data,用于存放數據元素,整數length表示線性表當前長度*/
    
    ElemType data[MAXSIZE];   /*定義線性表占用的數組空間*/
    int length;         /*線性表當前長度*/
    
}SqList;

typedef int Status; /*Status是函數的類型,其值是函數結果狀態碼,如OK等*/

// 用結構指針描述單鏈表
typedef struct Node{
    
    ElemType data;  // 數據域
    struct Node *Next; // 指針域
    
}Node;

// 取別名
typedef struct Node *LinkList;

通過這種鏈式存儲方式,若果p->data=ai,那么p->next->data=ai+1.

單鏈表的讀取(工作指針后移

  • 獲取鏈表第i個數據的算法思路:
    1.聲明一個結點p指向鏈表的第一個結點,初始化j從1開始;
    2.當j<i時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一結點,j+1
    3.若到鏈表末尾p為空,則說明第i個元素不存在;
    4.否則查找成功,返回結點p數據。
  • 代碼:
/**
 * 獲取鏈表第`i`個數據的
 */
Status GetElem(LinkList L, int i, ElemType *e){
    
    int j;
    LinkList p;  // 聲明指針p
    
    p = L->next;  // p指向鏈表L的第一個結點
    j = 1;      // 當前位置計數器設置為1
    
    while (p && j<i) {  // 當p不為空 切j<i時候 j繼續向后找
        
        p = p->next;
        j++;
    }
    
    if (!p || j>i) return ERROR;  // 到達結尾且沒找到
    
    *e = p->data;  // 獲取倒找的結果
    
    return OK;
}

單鏈表的插入算法

  • 算法思路:
    1.聲明一結點p指向鏈表頭結點,初始化j從1開始;
    2.當j<1時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一節點,j累加1;
    3.若到鏈表末尾p為空,則說明第i個元素不存在;
    4.否則查找成功,在系統中生成一個空節點s;
    5.將數據元素e賦值給s->data
    6.利用單鏈表插入語句插入:s->next = p->next; p->next = s;;
    7.返回成功。
  • 代碼:
/**
 * 單鏈表的插入
 */
Status ListInsert(LinkList *L, int i, ElemType e){
    
    int j;
    LinkList p,s;
    
    p = *L;
    j = 1;
    
    while (p && j<i) {  // 用于尋找第i個結點
        
        p = p->next;
        j++;
    }
    
    if (!p || j>i) return ERROR;  // 到達結尾且沒找到
    
    s = (LinkList)malloc(sizeof(Node));  //生成一個空節點s
    
    // 插入語句
    s->next = p->next;
    p->next = s;
    
    return OK;
}

單鏈表的刪除算法(前繼結點的指針繞過繞過后繼結點)

  • 算法實現思路:p->next = p->next->next,即 q = p->next; p->next = q->next
    1.聲明一結點p指向鏈表頭結點,初始化j從1開始;
    2.當j<1時,就遍歷鏈表,讓p的指針向后移動,不斷指向下一節點,j累加1;
    3.若到鏈表末尾p為空,則說明第i個元素不存在;
    4.否則查找成功,將欲刪除結點p->next賦值給q
    5.單鏈表的刪除標準語句:p->next = q->next;
    6.將結點中的數據賦值給e,作為返回;
    7.釋放q結點。
  • 代碼:
/**
 * 單鏈表的刪除
 */
Status ListDelete(LinkList *L, int i, ElemType e){
    
    int j;
    LinkList p,q;
    
    p = *L;
    j = 1;
    
    while (p && j<i) {  // 用于尋找第i個結點
        
        p = p->next;
        j++;
    }
    
    if (!p || j>i) return ERROR;  // 到達結尾且沒找到
    
    q = p->next;
    
    // 刪除語句
    p->next = q->next;
    free(q);
    
    return OK;
}

單鏈表的插入和刪除圖解

特點:先找到,再刪除或者增加。時間復雜度為o(1);

單鏈表的整表創建

對于順序存儲結構的線性表的整表創建,可以用數組的初始化來直觀理解。
但是對于單鏈表,和順序存儲結構就不一樣,不像順序存儲結構數據這么幾種,它的數據可以是分散在各個角落的,增長也是動態的。
對于每個鏈表來說,它所占用空間的大小和位置是不需要預先分配劃定的,可以根據系統的情況和實際的需要及時生成。

單鏈表整表創建的算法思路:

1.聲明一個結點p和計數器變量i;
2.初始化一個空鏈表L
3.讓L的頭結點的指針指向NULL,即建立一個帶頭結點的單鏈表;
4.循環實現后繼結點的賦值和插入。

一、頭插法建立單鏈表

頭插法從一個空表開始,生成新結點,讀取數據存放到新節點的數據域中,然后將新結點插入到當前鏈表的表頭,知道結束。

簡單來說,就是把新加進的元素放在表頭的第一個位置(如同插隊):

  • 先讓新節點的next指向頭結點之后;

  • 讓后讓表頭的next指向新節點。

  • 代碼:

/**
 * 頭插法
 */
void CreatListHead(LinkList *L, int n){

    LinkList p;
    int i;
    
    srand(time(0));   // 初始化隨機數種子
    
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    
    for (i = 0; i < n; i++) {
        
        p = (LinkList)malloc(sizeof(Node)); // 生成新節點
        p ->data = rand()%100 + 1;
        p->next = (*L)->next;
        (*L)->next = p;
        
    }
}

這里使用了c語言里面的生成隨機數的函數rand()來構造結點里面的數據。

二、尾插法建立單鏈表

頭插法建立鏈表隨讓算法簡單,但是生成的鏈表中結點的次序和輸入的順序相反。
若把新結點都插入到最后,那么這種算法就是尾插法。

  • 代碼:
/**
 * 尾插法
 */
void CreatListTail(LinkList *L, int n){
    
    LinkList p, r;
    int i;
    
    srand(time(0));
    *L = (LinkList)malloc(sizeof(Node));
    r = *L;
    
    for (i = 0; i < n; i++) {
        
        p = (Node *)malloc(sizeof(Node));
        p->data = rand()%100+1;
        r->next = p;
        r = p;
    }
    
    r->next = NULL;
}
尾插法圖解

單鏈表的整表刪除

  • 單鏈表的整表刪除的算法思路:
    1.聲明結點pq;
    2.將第一個結點賦值給p,下一結點賦值給q
    3.循環執行釋放p和將q賦值給p的操作。
  • 代碼:
Status ClearList(LinkList *L){

    LinkList p,q;
    
    p = (*L)->next;
    
    while (p) {
        q = p->next;
        free(p);
        p = q;
    }
    
    (*L)->next = NULL;
    
    return OK;
}

注意:整表刪除的時候,需要一個個結點的刪除。

單鏈表結構和順序存儲結構對比

存儲分配方式:
  • 順序存儲結構用一段連續的存儲單元依次存儲線性表的數據元素;
  • 單鏈表采用鏈式存儲結構,用一組任意的儲存單元存放線性表的元素;
時間性能:
  • 查找:
    • 順醋存儲結構O(1);
    • 單鏈表O(n);
  • 插入和刪除:
    • 順序存儲結構需要平均移動表長一般的元素,時間為O(n)
    • 單鏈表在計算機處某位置的指針后,插入和刪除時間為O(1);
空間性能
  • 順序存儲結構需要預分配存儲空間,分大了,容易造成空間浪費,分小了,容易發生溢出。
  • 單鏈表不需要分配存儲空間,只要有就可以分配,元素個數也不受限制。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容