C語言寫單鏈表的建立、釋放、追加(即總是在最後的位置增加節點)

來源:互聯網
上載者:User

標籤:理由   png   gpo   別人   定義   大神   pos   接下來   周末   

       昨天周末給學妹講了一些指標的知識,本來我對指標就是似懂非懂的狀態,經過昨天一講,我對指標的學習就更深刻了果然給別人講課也是學習的一個方法。加上最近複習資料結構,發現我的部落格裡沒有鏈表的博文,所以趁這時候加上一篇。

  在此之前,我們先談一下我要說的一些基本知識:

①函數參數為什麼是雙指標?

   我們先寫一下這麼一個程式:

# include<stdio.h>
void Gai(int m)
{
m=5;
}
int main(void)
{
int a=1;
Gai(a);
printf("%d\n",a);
return 0;
}

那麼我們可以得知,輸出a的值是1,為什麼我調用了函數,把a傳進去,並沒有變成5呢?這就是關鍵所在。我總結一下,形參m只是實參a的一個賦值的變數,形參我們都知道是函數調用時候才分配記憶體單元,當函數調用完畢後,形參就會被幹掉了,所以上面程式可以這麼理解:定義一個a變數,它的值為1,當把a作為實參傳進Gai這個函數時,系統會定義一個變數m,並且把a的值“1”賦給了m,然後又執行m=5。所以,到整個程式結束,m=5,a=1,所以a的值根本就沒有發生改變。所以說,在main函數裡,若想通過函數來改變變數的值,那是不可能的。

接下來我們把程式修改一下:

# include<stdio.h>
void Gai(int * m)
{
*m=5;
}
int main(void)
{
int a=1;
Gai(&a);
printf("%d\n",a);
return 0;
}

通過運行後我們可以看到a的值此時變成了“5”。所以,我們可以總結:

若一個變數想通過函數來改變本身的值,將本身作為參數傳遞是不成功的,只有傳遞本身的指標(地址)才能達到這樣的效果。

 

所以後面我們建立鏈表時,傳遞的是雙指標,這就是為什麼參數是雙指標的原因。

因為我之前也一直不明白,直到我昨天給學弟學妹們講課的時候,我才恍然大悟,所以我也算很笨了,所以在這裡給大家總結一下,因為我在別的部落格裡,看到也有挺多人不理解為什麼是雙指標,現在希望讀者們可以理解。

 

②每一個變數的記憶體單元中都有自己的地址

為了好理解,我畫圖把它綁在一塊,雖然可能物理結構上不是長這樣,但邏輯上是長這樣的,比如
   int a=2;

  int * p = &a;

所以說,只要是變數它都會有自己的地址(指標),即使是指標變數。

然後,指標它就是用來存地址的,只有兩部分,一部分是附帶自己的地址,一部分是存別人的地址

 

③指標就是地址,地址就是指標,指標類型的變數它的值只用來裝指標。

為什麼我會說這句話呢。因為之前,在昨天為止,我那麼久,居然一直都理解錯了,也怪我太笨了哈哈。比如說定義了節點類型

typedef struct n
{
int data; //資料域
struct n * next; //指標域
} Node;

然後 Node * L;  我一直以為L是長這樣子的

原來不是!!!  它不是!!  害死我了,以前我可糾結了好久了!!!太蠢了哈哈!!原來我一直以為什麼類型的指標就長什麼樣!!

不是的,其實它是什麼類型的指標它就存什麼樣的地址。而不是長成那樣,所以L其實是長這樣的:

 總之這個坑,如果你們已經會的,可以笑一下我,如果也一樣像我一樣掉坑的,希望看到這裡後能及時填坑。這個大大大大大的坑,嗨呀,氣死了。都怪以前沒認真學指標。

 

以上就是今天的預備知識,接下來就開始學習單鏈表的簡單操作了。我會用圖來結合,因為我一直強調圖和代碼結合,這樣才能學好資料

結構,這樣才能對資料結構有形象的想法,當然大神都是直接理解的,我高攀不起。我比較菜,就挖掘了自己的學習方法,嘿嘿。

 

單鏈表我採用了頭指標和頭結點的結構。

這次單鏈表的操作可能有些不一樣,但原理都是一樣的,或者說,把圖理解了,代碼也就理解了;

/*參數:頭指標的指標(雙指標)作用:初始化鏈表,使頭指標指向一個新結點,          這個新節點就是頭結點*/void InitHead(Node * *pHead)/*參數:頭指標,其實也是頭指標的拷貝作用:釋放整個鏈表*/void Free_list(Node * pHead)/*參數:頭指標,一個值作用:往鏈表的末段追加val*/void append(Node * pHead,int val)/*參數:頭指標作用:遍曆輸出鏈表(跳過頭結點)*/void Showlist(Node * pHead)

(一)初始化鏈表 

void InitHead(Node * *pHead) //為鏈表產生頭結點 使頭指標指向頭結點
{
*pHead = (Node *)malloc(sizeof(Node));
if(*pHead == NULL)
{
printf("頭結點分配失敗,程式終止! \n");
exit(-1);
}
(*pHead)->next=NULL;
}

在main函數裡面定義: Node * L = NULL; //定義一個指標,指向Node類型,其實也就是整個鏈表的頭指標

         然後調用      InitHead(&L);

 圖解如下:

*pHead = (Node *)malloc(sizeof(Node)); 

//其實*PHead就是頭指標L的值了,加*號就代表指標的值,malloc會申請一個結點,然後返回結點的首地址,其實這個新產生的結點是沒有名字的,為了方便

//理解,我們管它叫x  圖解如下:

至於PHead?哈哈,等這個函數結束後,它就被會幹掉了,所以到頭來,它只為他人作了嫁衣,不過這也正是它存在的意義。

如果傳遞的是單指標的話,pHead作為盜版的頭指標指向了那個新產生的結點,然後函數結束後,它們的狀態分別是:L  依然存在,什麼也沒變化。 pHead,被幹掉,徹徹底底的沒了,至於新產生的結點,則是孤零零的在記憶體區裡瑟瑟發抖,等待有緣人來指向它,所以這就是為什麼要用雙指標的理由。用了雙指標,L指向了新產生的結點,PHead被幹掉,皆大歡喜。

(二)釋放鏈表

void Free_list(Node * pHead) //釋放鏈表
{
Node * p;
while(pHead != NULL)
{
p = pHead;
pHead = pHead->next;
free(p);
p = NULL;
}
}

因為在鏈表中產生的新節點是用malloc的,所以要用free把它回收,malloc和free是一夫一妻。

在這種小程式或許不free也沒什麼太大的問題,但以後做項目時如果不回收就麻煩大了,所以養成free的習慣。

圖解如下:

 

 之後p就會變成  然後free(p)就是把p指向的那個結點,也就是圖中的頭結點,給幹掉,

而pHead也被函數結束後幹掉,而L只拿著一個head的地址但卻找不到人了;

這裡的圖是只有一個頭結點時的釋放,但即使有多個結點,也是一樣的做法,你們可以自己畫圖類比一下,加深記憶。

 

(三)向鏈表末端追加元素

void append(Node * pHead,int val)
{
Node * r=pHead;
Node * pNew = (Node *)malloc(sizeof(Node)); //產生新節點
if(pNew == NULL)
{
printf("新節點分配失敗,程式終止! \n");
exit(-1);
}
pNew->data=val;
pNew->next=NULL;

while(r->next != NULL) //讓尾指標迴圈直到最後一個節點
{
r=r->next;
}

r->next=pNew;
r=pNew;

}

這個代碼太長,有點難畫圖,我盡量吧,圖示如下:

然後定義一個指標r,把pHead複製過去

為了方便理解,我把所有新產生的結點都叫做x。

 然後定義一個PNew指標指向新產生的結點x;然後賦值,共置為NULL

其實pNew-<data就是x.data

然後這一句代碼

while(r->next != NULL) //讓尾指標迴圈直到最後一個節點
{
r=r->next;
}

這是為了讓r指標指向最後一個結點,

為什麼是r->next != NULL 而不是r!=NULL;這裡是有區別的,因為我這種追加的方法是屬於後插法。總之就是根據圖來寫代碼

r->next是這個

而r是這個

所以判斷的時候,應該判斷的是鏈表中的next;而不是判斷r有沒有指向誰;

接下來就是最後的連起來了。

r->next=pNew;
r=pNew;

r->next=pNew;//為什麼這裡它們明明是指向了PNew,但圖中卻指向x呢?我這麼理解不懂對不對,一個指標指向了一個結點,那麼這個指標就相當於這個結點了

然後最後一個

(四)遍曆輸出鏈表

void Showlist(Node * pHead)
{
pHead=pHead->next; //跳過頭結點輸出
while(pHead!=NULL)
{
printf("%d ",pHead->data);
pHead=pHead->next;
}
}

最後一個就不畫圖了,相信大家也能看懂。

至此,整篇文章應該寫完了。嘖嘖嘖嘖,去吃飯了。

有錯請在下方評論,咱們一起進步。

謝謝~

 忘了貼完整代碼了,測試你們自己測試一下,我這裡測試結果無誤

# include<stdio.h># include<stdlib.h>typedef struct n{    int data;             //資料域    struct n * next;   //指標域} Node;void InitHead(Node * *pHead)  //為鏈表產生頭結點 使頭指標指向頭結點{    *pHead = (Node *)malloc(sizeof(Node));    if(*pHead == NULL)    {        printf("頭結點分配失敗,程式終止! \n");        exit(-1);    }    (*pHead)->next=NULL;}void Free_list(Node * pHead)  //釋放鏈表{    Node * p;    while(pHead != NULL)    {        p = pHead;        pHead = pHead->next;        free(p);        p = NULL;    }}void append(Node * pHead,int val){    Node * r=pHead;    Node * pNew = (Node *)malloc(sizeof(Node));  //產生新節點    if(pNew == NULL)    {        printf("新節點分配失敗,程式終止! \n");        exit(-1);    }    pNew->data=val;    pNew->next=NULL;    while(r->next != NULL)  //讓尾指標迴圈直到最後一個節點    {        r=r->next;    }    r->next=pNew;    r=pNew;}void Showlist(Node * pHead){    pHead=pHead->next; //跳過頭結點輸出    while(pHead!=NULL)    {        printf("%d ",pHead->data);        pHead=pHead->next;    }}int main(void){    Node * L = NULL;    InitHead(&L);    append(L,1);    append(L,4);    append(L,7);    append(L,9);    append(L,332);    append(L,6);    append(L,235);    Showlist(L);    Free_list(L);    L=NULL;    return 0;}

  

C語言寫單鏈表的建立、釋放、追加(即總是在最後的位置增加節點)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.