1. 程式人生 > >連結串列的理解與實現[資料結構]

連結串列的理解與實現[資料結構]

一、連結串列的定義

n個節點離散分配,節點之間通過指標相連。除了首節點和尾節點之外,每個節點都只有一個前驅結點和一個後繼節點。
如下圖:
這裡寫圖片描述
大家有沒有發現,連結串列的結構很像一種交通工具,什麼呢?
火車。(節點==車廂,指標==車廂間相連的繩索)

二、連結串列的實現

注意,這裡實現的是連結串列中最簡單的單鏈表。

1、建立連結串列

1.1 定義連結串列最基本的組成元素–節點

我們由上面連結串列的定義可以知道,連結串列的每個節點應該含有兩部分:
1、每個節點本身儲存的資料,我們假定data 為 int 型別;
2、當前節點指向下一個節點的指標。

好,我們根據以上兩部分進行節點的定義:

/**
    NODE:節點型別
    PNODE:指向節點的指標
*/
typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE,*PNODE;

1.2建立連結串列

上面我們定義好了連結串列的節點,接下來我們就進入激動人心的一刻吧,建立屬於你的連結串列!
上面講了,連結串列的結構就像火車的結構,我們不妨以火車為例進行講解吧!

1、建立火車頭:

//PNODE是指向NODE節點的指標型別
PNODE pHead=(PNODE)malloc(sizeof(NODE));

完美!我們創造了一個火車頭,並且使用pHead指向了這個火車頭。
如下圖:
這裡寫圖片描述

2、建立火車尾部

    //PNODE是指向NODE節點的指標型別
    PNODE pHead=(PNODE)malloc(sizeof(NODE));
    //pTail是指向火車尾部的指標
    PNODE pTail=pHead;
    pHead->pNext=NULL;

由於現在只有一節車廂,所以pHead和pTail都指向這節車廂。
如下圖:
這裡寫圖片描述

3、新增車廂

//假設我們新增3節車廂
for(i=0;i<3;i++)
{
     printf("請輸入第%d節車廂的數值\n",i+1);
     scanf("%d",&val);
     PNODE pNew=
(PNODE)malloc(sizeof(NODE)); pNew->data=val; pTail->pNext=pNew; pTail=pNew; pNew->pNext=NULL; }

當i=0的時候,我們新增1節車廂,假設第1節車廂儲存的值為1。
結構從
這裡寫圖片描述

變為
這裡寫圖片描述

上述pTail=pNew,使得pTail指向pNew指向的節點。

當i=1的時候,我們新增第2節車廂,假設第2節車廂儲存的值為2。

這裡寫圖片描述

當i=3的時候,同理新增第3節車廂。
如此,3節車廂就新增完了。

注意,pTail的作用是始終指向最後一節車廂,方便新增新車廂。
新增車廂的時候直接讓pTail指向新的車廂,再讓pTail指向新新增的車廂(最後一節車廂)。

總體程式碼:

//
typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE,*PNODE;
//建立連結串列
PNODE createList()
{
    int i,val,n;
    PNODE pHead=(PNODE)malloc(sizeof(NODE));
    PNODE pTail=pHead;
    pHead->pNext=NULL;
    printf("請輸入連結串列長度:");
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        printf("請輸入第%d個數值\n",i+1);
        scanf("%d",&val);
        PNODE pNew=(PNODE)malloc(sizeof(NODE));
        pNew->data=val;
        pTail->pNext=pNew;
        pTail=pNew;
        pNew->pNext=NULL;
    }
    //將指向火車頭的指標返回
    return pHead;
}

2、遍歷連結串列

2.1判斷連結串列是否為空

我們要遍歷一個連結串列,首先要判斷連結串列是否為空。
連結串列空的話,我們就直接返回,不遍歷。

/**
    返回1:連結串列為空
    返回0:連結串列不空
*/
//判斷連結串列是否為空
int isEmpty(PNODE pHead)
{
    if(pHead->pNext==NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

2.2遍歷連結串列

如果連結串列不空的話,我們就進行如下遍歷

PNODE p=pHead->pNext;
while(p!=NULL)
{
    printf("%d ",p->data);
    p=p->pNext;
}

我們定義一個指標p
PNODE p=pHead->pNext;使得p指向了第1節車廂
這裡寫圖片描述

下面,只要p不為空(p!=NULL)
即p有所指向,那麼就列印p所指向的車廂的裡面的data值。
然後,p再指向下一節車廂(p=p->pNext)
這裡寫圖片描述

迴圈上面步驟,直到p指向最後一節車廂
這裡寫圖片描述

再迴圈,p指向第4節車廂。
唉?第4節車廂沒了!
那麼p==NULL了,退出while迴圈。至此,迴圈遍歷完畢。

完整程式碼如下:

//判斷連結串列是否為空
int isEmpty(PNODE pHead)
{
    if(pHead->pNext==NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
//遍歷連結串列
void traverseList(PNODE pHead)
{
    if(isEmpty(pHead))
    {
        printf("連結串列為空\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        while(p!=NULL)
        {
            printf("%d ",p->data);
            p=p->pNext;
        }
        free(p);
    }
}

3、刪除連結串列

假設p指向第1節車廂,q指向第2節車廂。
比如我們要刪除第2節車廂。
如圖:
這裡寫圖片描述

即:
p->pNext=q->pNext;(p指向第1節車廂,q指向第2節車廂)
使第1節車廂的pNext指向第3節車廂,就刪除了第2節車廂。

整體程式碼為:

//刪除任意位置的元素
void deleteList(PNODE pHead,int pos)
{
    if(isEmpty(pHead))
    {
        printf("連結串列為空,不可刪除\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        //注意這裡的i=2(因為p指向的是要刪除車廂的前一節車廂)
        int i=2;
        while(p!=NULL && i!=pos)
        {
            p=p->pNext;
            i++;
        }
        PNODE q=p->pNext;
        p->pNext=q->pNext;
        free(q);
    }
}

三、整體程式碼

#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE,*PNODE;
//建立連結串列
PNODE createList()
{
    int i,val,n;
    PNODE pHead=(PNODE)malloc(sizeof(NODE));
    PNODE pTail=pHead;
    pHead->pNext=NULL;
    printf("請輸入連結串列長度:");
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        printf("請輸入第%d個數值\n",i+1);
        scanf("%d",&val);
        PNODE pNew=(PNODE)malloc(sizeof(NODE));
        pNew->data=val;
        pTail->pNext=pNew;
        pTail=pNew;
        pNew->pNext=NULL;
    }
    return pHead;
}
//判斷連結串列是否為空
int isEmpty(PNODE pHead)
{
    if(pHead->pNext==NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
//遍歷連結串列
void traverseList(PNODE pHead)
{
    if(isEmpty(pHead))
    {
        printf("連結串列為空\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        while(p!=NULL)
        {
            printf("%d ",p->data);
            p=p->pNext;
        }
        free(p);
    }
}
//刪除任意位置的元素
void deleteList(PNODE pHead,int pos)
{
    if(isEmpty(pHead))
    {
        printf("連結串列為空,不可刪除\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        int i=2;
        while(p!=NULL && i!=pos)
        {
            p=p->pNext;
            i++;
        }
        PNODE q=p->pNext;
        p->pNext=q->pNext;
        free(q);
    }
}
int main()
{
    PNODE pHead=createList();
    bubbleSort(pHead);
    traverseList(pHead);
    return 0;
}

四、總結

從以上實現可以看出,連結串列的刪除非常簡便,但是查詢很慢,很繁瑣,需要從pHead一個個查起。

優點:插入刪除簡單,花費時間短。空間沒有限制。
缺點:查詢慢,比陣列多一個pNext域,所佔總空間大。

線性結構有陣列和連結串列。陣列和連結串列各有優缺點。沒有哪一個最好。也就是沒有最好,只有更好,看哪個更適合你的需求,就選擇哪個結構。

本人初學,很多問題不甚明瞭。
如有錯誤或不當之處,敬請您不吝指正,
如果有問題需要探討,煩請留言回覆。