關於c語言單項鍊表尾添加

猶豫了幾天,看了不少大牛寫的關於c語言鏈表,感觸不少,終於下定決心,把本身對於鏈表的理解隨之附上,可用與否,自行裁奪。因爲做者水平有限也是第一次寫,不足之處,竭誠但願獲得各位大神的批評指正。製做不易,不喜勿噴,謝謝!!!數組

在正文開始以前,我先對數組和鏈表進行簡單的對比分析。數據結構

 鏈表也是一種很常見的數據結構,不一樣於數組的是它是動態進行存儲分配的一種結構。數組存放數據時,必需要事先知道元素的個數。舉個例子,好比一個班有40我的,另外一個班有100我的,若是要用同一個數組前後來存放這兩個班的學生數據,那麼必須得定義長度爲100的數組。若是事先不肯定一個班的人數,只能把數組定義的足夠大,以能存聽任何班級的學生數據。這樣就很浪費內存,並且數組對於內存的要求必須是是連續的,數據小的話還好說,數據大的話內存分配就會失敗,數組定義固然也就失敗。還有數組對於插入以及刪除元素的效率也很低這就不一一介紹了。然而鏈表就相對於比較完美,它很好的解決了數組存在的那些問題。它儲存數據時就不須要分配連續的空間,對於元素的插入以及刪除效率就很高。能夠說鏈表對於內存就是隨用隨拿,不像數組要事先申請。固然,有優勢就必然有缺點,就好比說鏈表裏每個元素裏面都多包含一個地址,或者說多包含一個存放地址的指針變量,因此內存開銷就很大。還有由於鏈表的內存空間不是連續的,因此想找到其中的某一個數據就沒有數組那麼方便,必須先獲得該元素的上一個元素,根據上一個元素提供的下一元素地址去找到該元素。因此不提供「頭指針」(下文中「頭指針」爲「PHead」),那麼整個鏈表將沒法訪問。鏈表就至關於一條鐵鏈一環扣一環(這個稍後會詳細的說)。函數

 

 鏈表

上面我提到過鏈表是動態進行存儲分配的一種結構。鏈表中的每個元素稱爲「結點」,每一個結點都包括兩部分:一部分爲用戶須要的實際數據,另外一部分爲下一結點的地址。鏈表有一個「頭指針(PHead)」變量,存放着一個地址,該地址指向第一個結點,第一個結點裏面存放着第二個結點的地址,第二個結點又存放着第三個結點地址。就這樣頭指針指向第一個結點,第一個結點又指向第二個......直到最後一個結點。最後一個結點再也不指向其餘結點,地址部分存放一個「NULL」。 見下圖:(表中有一個尾指針(PEnd)其做用後面會解釋)spa

 

c語言單項鍊表尾添加總體代碼以下:(詳解附後)

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>


//函數聲明
//尾添加
void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int shu_ju);
//尾添加(沒有尾指針)
void wei_tian_jia_(struct NODE** PHEAD, int shu_ju);
//釋放鏈表
void shi_fang_lian_biao(struct NODE* PHEAD);
//釋放鏈表(並是頭指針(PHead)尾指針(PEnd)指向空)
void shi_fang_lian_biao_free(struct NODE** PHEAD, struct NODE** PEnd);
//輸出鏈表
void shu_chu(struct NODE* PHEAD);


//定義一個鏈表結構體
struct NODE
{
    int shu_ju;           //用戶須要的實際數據
    struct NODE* PNext;   //下一結點的地址

};


int main(void)
{
    //建立頭尾指針
    struct NODE* PHead = NULL;
    struct NODE* PEnd = NULL;


    //尾添加
    wei_tian_jia(&PHead, &PEnd, 17);
    wei_tian_jia(&PHead, &PEnd, 21);
    wei_tian_jia(&PHead, &PEnd, 34);
    wei_tian_jia(&PHead, &PEnd, 8);
    wei_tian_jia(&PHead, &PEnd, 24);

    //尾添加(沒有尾指針)
    //wei_tian_jia_(&PHead, 23);
    //wei_tian_jia_(&PHead, 17);
    //wei_tian_jia_(&PHead, 11);

    //輸出鏈表
    shu_chu(PHead);

    //釋放鏈表
    //shi_fang_lian_biao(PHead);

    //釋放鏈表(並是頭指針(PHead)尾指針(PEnd)指向空)
    shi_fang_lian_biao_free(&PHead, &PEnd);


    system("pause");
    return 0;
}

//尾添加 void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int SHU_JU) { //建立結點 struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE)); if (PTEMP != NULL) { //節點賦值 PTEMP->shu_ju = SHU_JU; PTEMP->PNext = NULL; //把結點連起來 if (NULL == *PHEAD) // 由於PHEAD若是是NULL的話 PEND也就是NULL 因此條件裏面沒必要要寫 { *PHEAD = PTEMP; *PEND = PTEMP; } else { (*PEND)->PNext = PTEMP; *PEND = PTEMP; } } } //尾添加(沒有尾指針) void wei_tian_jia_(struct NODE** PHEAD1, int SHU_JU) { //建立結點 struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE)); if (PTEMP != NULL) { //結點成員賦值 PTEMP->shu_ju = SHU_JU; PTEMP->PNext = NULL; //把結點連一塊兒 if (NULL == *PHEAD1) { *PHEAD1 = PTEMP; } else { struct NODE* PTEMP2 = *PHEAD1; while (PTEMP2->PNext != NULL) { PTEMP2 = PTEMP2->PNext; } PTEMP2->PNext = PTEMP; } } } //輸出鏈表 void shu_chu(struct NODE* PHEAD) { while (PHEAD != NULL) { printf("%d\n", PHEAD->shu_ju); PHEAD = PHEAD->PNext; } } //釋放鏈表 void shi_fang_lian_biao(struct NODE* PHEAD) { struct NODE* P = PHEAD; while (PHEAD != NULL) { struct NODE* PTEMP = P; P = P->PNext; free(PTEMP); } free(PHEAD); } //釋放鏈表(並是頭指針(PHead)尾指針(PEnd)指向空) void shi_fang_lian_biao_free(struct NODE** PHEAD, struct NODE** PEnd) { while (*PHEAD != NULL) { struct NODE* PTEMP = *PHEAD; *PHEAD = (*PHEAD)->PNext; free(PTEMP); } *PHEAD = NULL; *PHEAD = NULL; }

 

 

部分代碼詳解:

(再次申明:因爲做者水平有限,因此有的變量名用的拼音。見笑,莫怪!!!爲了簡單明瞭,方便起見,我定義了一個實際數據。)

 

「頭指針」(PHead)以及「尾指針」(PEnd):

 

 

 

 頭指針很好理解指向首結點用於遍歷整個數組,而尾指針呢?咱們先看下面兩段代碼一段是有尾指針的一段是沒有尾指針的:指針

 顯然這是一段有尾指針的代碼。這裏的思想就是當寫入第一個成員進鏈表的時候,此時鏈表就一個成員,便是頭(PHEAD),也是尾(PEND),當寫入第二個成員的時候,鏈表頭(PHEALD)不動鏈表尾(PEND)向後移,指向最後一個結點。
//尾添加
void wei_tian_jia(struct NODE** PHEAD, struct NODE** PEND, int SHU_JU)
{
    //建立一個結點
    struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE));
    if (PTEMP != NULL)
    {
        //節點成員賦值(必定要每一個成員都要賦值)
        PTEMP->shu_ju = SHU_JU;
        PTEMP->PNext = NULL;

        //把結點連起來
        if (NULL == *PHEAD) // 由於PHEAD若是是NULL的話 PEND也就是NULL  因此條件裏面沒必要要寫
        {
            *PHEAD = PTEMP;
            *PEND = PTEMP;
        }
        else
        {
            //把尾指針向後移
            (*PEND)->PNext = PTEMP;
            *PEND = PTEMP;

        }

    }
    
}

那麼下面這段代碼是沒有尾指針的。它的思想就是頭指針一直指向第一個結點,而後經過遍從來找到最後一個結點,從而使最後一個結點裏面的指針指向所要插入的元素。code

//尾添加(沒有尾指針)
void wei_tian_jia_(struct NODE** PHEAD1, int SHU_JU)
{
    //建立結點
    struct NODE* PTEMP = (struct NODE*)malloc(sizeof(struct NODE));

    if (PTEMP != NULL)
    {
        //結點成員賦值
        PTEMP->shu_ju = SHU_JU;
        PTEMP->PNext = NULL;

        //把結點連一塊兒    
        if (NULL == *PHEAD1)
        {
            *PHEAD1 = PTEMP;

        }
        else 
        {
            struct NODE* PTEMP2 = *PHEAD1;
            while (PTEMP2->PNext != NULL)
            {
                PTEMP2 = PTEMP2->PNext;
            }
            PTEMP2->PNext = PTEMP;
        }

    }

}

我把上面代碼裏面的一段摘出來講明一下。blog

這段代碼裏面能夠看到我又定義了一個PTEMP2指針變量,爲何呢?前面我提到過沒有尾指針的時候添加結點的思想就是要遍歷數組,從而找到最後一個結點而後讓它指向咱們要插入的結點,若是沒有這個PHEAD2,咱們遍歷完鏈表之後咱們的頭指針PHEAD1就已經指向了最後一個結點了,單項鍊表若是頭指針移動了,數據就會找不到了。因此我定義了一箇中間變量裝着頭指針而後去遍歷鏈表,讓頭指針永遠指向鏈表的頭。內存

        else 
        {
            struct NODE* PTEMP2 = *PHEAD1;
            while (PTEMP2->PNext != NULL)
            {
                PTEMP2 = PTEMP2->PNext;
            }
            PTEMP2->PNext = PTEMP;
        }

 

能夠看到有尾指針的代碼和沒有尾指針的代碼裏面,有尾指針的鏈表裏面我每次添加完數據都讓尾指針指向最後一個結點,而後經過尾指針來添加數據。而沒有尾指針的鏈表裏面每次添加數據都要經過循環來遍歷鏈表找到最後一個結點而後指向所添加的結點。若是一個鏈表裏面有幾萬個結點,每次都經過循環遍歷鏈表來添加數據,那麼速度就相對於有尾指針的鏈表慢不少。總而言之,仍是看我的愛好吧。無論黑貓仍是白貓能抓到耗子都是好貓。io

相關文章
相關標籤/搜索