C++鏈表(一)小白的理解

要想理解鏈表,一定得理解指針。鏈表的本質就是指針。

關於指針的理解,小白可以看這篇文章,比較容易理解。https://blog.csdn.net/u014095878/article/details/104559879

要想學習指針,第一步,就是學會接受它,不要覺得它很難。第二步,要立體的去思考,要想着這個指針裏面裝了什麼東西,而不是簡單的給這個變量賦值。

創建單鏈表有四種情況:不帶頭節點的頭插法,不帶頭節點的尾插法,帶頭節點的頭插法,帶頭節點的尾插法。

看完下面我的分析。你就可以掌握單向鏈表的創建了。

先來看一個不帶頭節點的尾插法的實例:

#include <stdio.h>
#include <malloc.h>
#include<iostream>
using namespace std;

typedef int ElemType;
typedef struct Node_s        //定義鏈表節點的結構體
{
    ElemType data;           //數據域
    struct Node_s* next;     //指針域

}Node, * LinkList;             //Node相當於struct Node_s    Linklist相當於struct Node_s *

int Output_L(LinkList head)  //輸出不帶頭節點的單鏈表的元素
{
    if (!head)
        return -1;

    LinkList p;
    p = head;

    while (p != NULL)
    {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
    return 0;
}

int main()
{
    int  i;
    Node *head = NULL;
    Node *tail = NULL;
    Node *new_node = NULL;

    for(i=0;i<5;i++)
    {
        new_node = (LinkList)malloc(sizeof(Node));
        new_node->data = i+1;
        new_node->next = NULL;     //將即將要插入的節點打包(數據域放什麼,指針域放什麼)

        if(head == NULL)              //如果爲空鏈表
        {
            head = new_node;        //頭指針指向第一個節點,頭結點
            tail = new_node;        //尾指針指向第一個節點
        }
        else
        {
            tail->next = new_node;  //尾指針指向的節點(上一個節點)的指針域指向新插入的節點
            tail = new_node;        //這兩句不能反
        }
        cout << "i =" << i << ",tail = " << tail << endl;
    }

    Output_L(head);
}

剛接觸這個很難去理解這個鏈表。我們知道鏈表是由一個結點一個結點串起來的。也知道一個結點有兩部分組成,一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。卻不知道具體是是怎麼實現的,儘管有一堆代碼告訴我們實現的方法,我們卻難以消化。

剛看到這個代碼時,我只理解一句,new_node = (LinkList)malloc(sizeof(Node));這個是申請一塊內存。

說說不理解的點。

1.head只賦值了一次,爲什麼後面打印輸出是一個完整的單向鏈表?

2.爲什麼tail->next = new_node;賦值之後又賦值tail = new_node; ?

 

 

帶着這兩個疑問,我們重新梳理一下上面的代碼。

Node *head = NULL;
Node *tail = NULL;
Node *new_node = NULL;

有3個變量,一個是head 頭指針,一個是tail尾指針,一個是new_node不斷的申請新內存的指針。

1.第一次進循環來,i=0;我們已經理解了new_node = (LinkList)malloc(sizeof(Node));每循環一次就申請一個新內存,也就新節點的地址;

 

2.new_node->data = i+1;給數據域賦值。i=0的時候,這個時候我們知道了第一個結點的地址是什麼,地址裏存放的數據域是什麼,但是還不知道下一個結點的地址。

if(head == NULL)              //如果爲空鏈表
{
    head = new_node;        //頭指針指向第一個節點,頭結點
    tail = new_node;        //尾指針指向第一個節點
}

 

3.知道了第一個結點的地址,也就是頭結點,趕緊把這個地址賦值給head(head = new_node;)。

我們疑惑的問題點到了,爲什麼只賦值一次?

因爲,頭結點賦值了之後,就不必再變了。後面變化的一直是tail尾指針,因爲你不斷的往後面插入數據。所以,尾指針是一直變化的。

這裏我們也看到了,tail第一次跟head指向的地址一樣(tail = new_node;)。

這個時候第一個結點的數據域知道了,但是指針域還不知道,head->next = null,tail->next = null,new_node->next = null,還不知道下一個地址在哪裏。

 

4.緊接着,第二次進來,i= 1,new_node又申請了新的內存地址,即第二個結點。

new_node->data = i+1 =2;這個時候tail還是指向第一個地址(頭結點),第一個地址存放的數據tail->data=1,tail->next=null.

所以這個時候,給tail->next賦值,tail->next = new_node,

把第一個結點的指針指向第二個結點,這個時候就串起來鏈表了。但是隻有兩個結點的鏈表。

後面是我們的第二個問題點,爲什麼又賦值tail = new_node; 因爲後面還要繼續往後面插入數據。

tail已經完成了第一次兩個結點的串聯,tail這個時候要移到第二個結點來,它要搬家了。

head頭結點地址不變,tail從頭結點地址移到第二個結點的地址,專業術語是,指向第二個結點的地址。

 else
 {
      tail->next = new_node;  //尾指針指向的節點(上一個節點)的指針域指向新插入的節點
      tail = new_node;        //這兩句不能反
 }

 

如果還不理解的,可以看看這段代碼。我們知道指針就是存地址的嘛。

    int i = 1,j=2;
    int *p = &i;
    cout<<"p的地址:"<<p<<endl;
    p = &j;
    cout<<"p的地址:"<<p<<endl;

指針p的地址從指向i的地址,到第二次指向j的地址。

tail從第一個結點的地址,指向第二個結點的地址。結點地址裏存着數據data和一個地址,這個地址指向下一個結點的地址。

後面的循環,你可以自己在草稿紙裏演算,演算成功了,你就掌握了這個鏈表的創建了。

 

如果你還想了解單鏈表的其他的不帶頭節點的頭插法,帶頭節點的頭插法,帶頭節點的尾插法,請參看下面這位博主的這篇文章

https://blog.csdn.net/TAlice/article/details/82112921

感謝這位博主的介紹。感謝C/C++小白基礎交流羣裏@雁的指導幫助。希望你們能完全掌握鏈表。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章