連結串列的理解與實現[資料結構]
一、連結串列的定義
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域,所佔總空間大。
線性結構有陣列和連結串列。陣列和連結串列各有優缺點。沒有哪一個最好。也就是沒有最好,只有更好,看哪個更適合你的需求,就選擇哪個結構。
本人初學,很多問題不甚明瞭。
如有錯誤或不當之處,敬請您不吝指正,
如果有問題需要探討,煩請留言回覆。