一、鏈表原理
(1)鏈表的組成
鏈表是由若干個節點組成的(鏈表的各個節點結構是完全類似的),節點是由有效數據和指針組成的。有效數據區域用來存儲信息完成任務的,指針區域用於指向鏈表的下一個節點從而構成鏈表。
(2)鏈表的作用
時刻謹記:鏈表就是用來解決數組的大小不能動態擴展的問題,所以鏈表其實就是當數組用的。直白點:鏈表能完成的任務用數組也能完成,數組能完成的任務用鏈表也能完成。但是靈活性不一樣。
簡單說:鏈表就是用來存儲數據的。鏈表用來存數據相對於數組來說優點就是靈活性,需要多少個動態分配多少個,不佔用額外的內存。數組的優勢是使用簡單(簡單粗暴)。
二、鏈表構成
1、單鏈表的實現
(1)單鏈表是由節點組成的,節點中包含:有效數據和指針。
struct node
{
int data;
struct node *pNext;
}
(2)定義的struct node只是一個結構體,本身並沒有變量生成,也不佔用內存。結構體定義相當於爲鏈表節點定義了一個模板,但是還沒有一個節點,將來在實際創建鏈表時需要一個節點時用這個模板來複制一個即可。
(3) 頭指針pheader(每個鏈表都會有一個頭)
頭指針並不是節點,而是一個普通指針,只佔4字節。頭指針的類型是struct node *類型的,所以它才能指向鏈表的節點。
2、創建一個鏈表節點
(1)鏈表的內存要求比較靈活,不能用棧,也不能用data數據段。只能用堆內存。
(2)用堆內存來創建一個鏈表節點的步驟:
1、申請堆內存,大小爲一個節點的大小(檢查申請結果是否正確);
2、清理申請到的堆內存;
3、把申請到的堆內存當作一個新節點;
4、填充你哦個新節點的有效數據和指針區域。
代碼如下:
struct node *pHeader = NULL;
/********************************************************************/
// 每創建一個新的節點,把這個新的節點和它前一個節點關聯起來
// 創建一個鏈表節點 malloc返回一個int指針,指向存放數據的地址,這裏強制轉換
struct node *p = (struct node *)malloc(sizeof(struct node));
if (NULL == p)
{
printf("malloc error.\n");
return -1;
}
// 清理申請到的堆內存
bzero(p, sizeof(struct node));
// 填充節點
p->data = 1;
p->pNext = NULL; // 將來要指向下一個節點的首地址
// 實際操作時將下一個節點malloc返回的指針賦值給這個
pHeader = p; // 將本節點和它前面的頭指針關聯起來
3、鏈表頭尾插入分析代碼
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
struct node
{
int data ;
struct node *pNext;
};
struct node *pHeader = NULL;
// 作用:創建一個鏈表節點
// 返回值:指針,指針指向我們本函數新創建的一個節點的首地址
struct node * creat_node(int data)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
if(NULL == p)
{
printf("fail/n");
return NULL;
}
bzero(p,sizeof(struct node));
p->data = data;
p->pNext = NULL;
return p;
}
//作用:尾部插入一個節點
void insert_tail(struct node *pH,int data)
{
struct node *p = pH;
while(NULL != p->pNext) //找到最後一個指向NULL的指針,就是鏈表的最後一個節點
{
p = p->pNext; //這也是鏈表遍歷的方法
}
p->pNext = creat_node(data); //將新節點插入到最後一個節點尾部
}
void insert_head(struct node *pH,int data)
{
struct node *p = pH;
struct node *new = creat_node(data);
//新的節點的pNext指向空節點之前指向的節點首地址
new->pNext = pH->pNext;
//將空節點的pNext指針指向新節點的首地址;pH->pNext裏面的值就是結構體指針
//new裏面的值(新節點malloc後在內存中分配的實際地址)
pH->pNext = new;
}
int main()
{
pHeader = creat_node(0);
insert_tail(pHeader,5);
insert_head(pHeader,8);
insert_tail(pHeader,6);
printf("pHeader->data = %d\n",pHeader->data);
printf("pHeader->data = %d\n",pHeader->pNext->data);
printf("pHeader->data = %d\n",pHeader->pNext->pNext->data);
printf("pHeader->data = %d\n",pHeader->pNext->pNext->pNext->data);
}
4、鏈表刪除節點
(1)首先我們傳入一個鏈表的頭指針,傳入的鏈表有N個節點
int delate_link(struct node *pH,int data)
{
struct node *p = pH;
struct node *pPrev = NULL;
while(NULL != p -> pNext)
{
//將先前的指針保存,不能保存pPrev = p ->pNext,p ->pNext已經指向了下一個節點的首地址
//所以pPrev保存的是下一個節點的首地址,pPrev = p; 之後再pPrev ->pNext
pPrev = p; //保存被刪節點前面的節點首地址
p = p -> pNext;
if(p -> data == data)
{
pPrev -> pNext = p -> pNext; //被刪節點前節點pNext指向被刪節點的後一個節點的首地址
free(p);
return 0;
}
}
return 0;
}
5 鏈表的反轉
int reserve(struct node *pH)
{
struct node *p = pH->pNext;
struct node *plast;
if(NULL == p || NULL == p -> pNext)
return 0;
while(NULL != p -> pNext)
{
plast = p -> pNext;
if(p == pH->pNext)
{
p -> pNext = NULL;
}
else
{
p -> pNext = pH -> pNext;
pH -> pNext = p;
}
p = plast;
}
insert_head(pHeader,p->data);
}
6,雙鏈表與單鏈表對比
(1)很多時候我們都用的是雙鏈表,雖然每個節點多佔用一個指針(4個字節),但是可以忽略,雙鏈表在做刪除節點和插入節點等操作時,會快很多,比如不需要定義plast,pPrev,去保存可能被斷開的節點地址,因爲他有雙向指針,裏面有前前後後所以地址的保存。