轉載請註明出處,部份內容引自百度百科、譚浩強《C程序設計》、蝸牛君的奮鬥史大神的博客node
前置知識: C語言入門數組
數組黨的福音(本蒟蒻學鏈表時不會指針,然而好像全部人都拿指針寫)
首先,咱們須要知道什麼是鏈表
百度百科
看不懂勿噴(畢竟百度百科也不是用來讓人看懂的)
咱們能夠從中得出鏈表的特性:
鏈表是一種物理存儲單元上非連續、非順序的存儲結構
提取主謂賓:鏈表是存儲結構。我認爲這就是鏈表的本質——一種數據結構。
那麼非連續、非線性有什麼含義呢?這代表鏈表的內存是不連續的,前一個元素存儲地址的下一個地址中存儲的不必定是下一個元素。鏈表經過一個指向下一個元素地址的引用將鏈表中的元素串起來。
鏈表分爲三類:單向鏈表、雙向鏈表、循環鏈表
1,單向鏈表
單向鏈表是最簡單的鏈表形式。咱們將鏈表中最基本的數據稱爲節點(node),每個節點包含了數據塊和指向下一個節點的指針,鏈表有一個頭指針變量,圖中以head表示。能夠看出,head指向第一個元素,第一個元素又指向第二個元素……直到最後一個元素,該元素再也不指向其餘元素,它稱爲表尾,它的地址部分爲空,鏈表到此結束。
能夠看到,要找鏈表中的某一元素,必須先找到上一個元素,根據它提供的下一個元素的地址才能找到下一個元素。若是不提供頭指針,則整個鏈表都沒法訪問。鏈表如同一條鐵鏈同樣,一環扣一環,中間是不能斷開的。
爲了理解什麼是鏈表,打一個通俗的比方:幼兒園的老師帶領孩子們出來散步,老師牽着第一個小孩的手,第一個小孩的另外一隻手牽着第二個孩子……這就是一個鏈,最後一個孩子有一隻手空着,他是鏈尾。要找到這個隊伍,必須先找到老師,而後順序找到每個孩子。
變量聲明:數據結構
const int maxn=1010; struct node{ //point即指針,data就是須要維護的數據
int point,data; }a[maxn]; int head,cnt; //head即頭指針,cnt即內存池計數器
創建:spa
head=++cnt; //把head設爲沒有實際意義的哨兵
a[head].data=0;
插入(插入數據now到第k個元素以後):.net
add(++k,now); //進入,由於計算時考慮了哨兵,因此進入時++k
void add(int k,int now) { for(int i=head;i;i=a[i].point) //從頭指針開始遍歷鏈表
if(!(--k)) //到達插入位置
{ a[++cnt].point=a[i].point; //將新插入節點的指針指向插入位置的後繼
a[i].point=cnt; //將前驅節點的指針指向新插入節點
a[cnt].data=now; break; } }
刪除(刪除第k個元素):設計
del(++k); //進入
void del(int k) { for(int i=head;i;i=a[i].point) if((--k)==1) //找到前驅
{ a[i].point=a[a[i].point].point; //將前驅的指針指向後繼
break; } }
遍歷鏈表:指針
for(int i=a[head].point;i;i=a[i].point) cout<<a[i].data<<endl;
2,雙向鏈表
顧名思義,雙向鏈表就是有兩個方向的鏈表。同單向鏈表不一樣,在雙向鏈表中每個節點不只存儲指向下一個節點的指針,並且存儲指向前一個節點的指針。它的優勢是訪問、插入、刪除更方便。但「是以空間換時間」。
變量聲明:code
struct node{ int pre,nxt,data; //前驅和後繼
}a[maxn]; int head,cnt;
插入:blog
void add(int k,int now) { for(int i=head;i;i=a[i].nxt) if(!(--k)) { a[++cnt].nxt=a[i].nxt; //這兩個和單鏈表同樣
a[i].nxt=cnt; a[a[cnt].nxt].pre=cnt; //將後繼的前驅更新爲新插入節點
a[cnt].pre=i; //將新插入節點的前驅設爲其前驅
a[cnt].data=now; break; } }
刪除:內存
void del(int k) { for(int i=head;i;i=a[i].nxt) if(!(--k)) { a[a[i].pre].nxt=a[i].nxt; a[a[i].nxt].pre=a[i].pre; break; } }
3,循環鏈表
(1),單向循環鏈表
最後一個節點的指針指向頭結點
(2),雙向循環鏈表
最後一個節點的指針指向頭結點,且頭結點的前驅指向最後一個結點
代碼實現就再也不給出,相信你們已經可以實現。