鏈表
簡單鏈表的構成:頭指針(Header),若干個節點(節點包括了數據域和指針域),最後一個節點要指向空(NULL)。
實現原理:頭指針指向鏈表的第一個節點,然後第一個節點中的指針域指向下一個節點,然後依次指到最後一個節點,這樣就構成了一條鏈表。
鏈表的數據結構:
struct list_node
{
int data;//數據域,用於存儲數據
struct list_node *next;//指針,可以訪問節點數據,也可以遍歷,指向下一個節點
};
鏈表使用中:不知道如何聲明?
1.先聲明頭指針:list_single node*=NULL;
2.然後再去開闢空間:node=(list_single*)malloc(sizeof(list_single));
3.利用memset庫函數來快速清空結構體:
void *memset(void *s,int c,size_t n)
—>它的意思是:
將已經開闢內存空間 s 的首 n 字節的值設爲值 c 。
即:memset(node,0,sizeof(list_single));//清理一下
4.鏈表賦值:
node->data=100;
node->next=NULL;
5.使用鏈表:printf("%d\n",node->data);
6.釋放掉空間,結束程序。free(node);
創建一個節點:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list_node
{
int data;
struct list_node *next;
};
typedef struct list_node list_single;
int main(void)
{
list_single *node=NULL;
node=(list_single*)malloc(sizeof(list_single));
if(node==NULL){//?
printf("malloc fair!");
}
memset(node,0,sizeof(list_single));
node->data=100;
node->next=NULL;
printf("%d,%s",node->data,node->next);
return 0;
}
這只是創建了一個鏈表的節點,我們可將創建的過程封裝成函數,創建時調用即可!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list_node
{
int data;
struct list_node *next;
};
typedef struct list_node list_single;
list_single *creat_list_node(int data);//返回一個指針,即地址!
int main(void)
{
int data=100;
list_single *node=creat_list_node(data);
printf("%d,%s",node->data,node->next);
return 0;
}
list_single *creat_list_node(int data)
{
list_single *node=NULL;
node=(list_single*)malloc(sizeof(list_single));
memset(node,0,sizeof(list_single));
node->data=data;
node->next=NULL;
return node;
}
我們現在只是創建了一個鏈表節點,此時這個單節點鏈表在內存是這個樣子的:定義了一個頭指針指向(data,next)這個數據域,這個數據域的指針域next指向了NULL。
( * node)->(data,next)->NULL
下面我們來創建多個單鏈表節點,實現增刪改查!!!
1.首先頂一個單鏈表的數據結構!
與上面相同,只需要把上面的crate_list_node函數創建即可。
2.單鏈表的尾插:(鏈表的連接)
即實現:header->next=new
第一步:獲取當前節點的位置,也就是訪問頭節點
struct list * p=header;
第二步:判斷是否爲最後一個節點,如果不是,移動到下一個節點,如果是,將數據插入尾部。
while(NULL!=p->next) p=p->next;
p->next=new;
3.單鏈表的頭插:(鏈表的插入)
頭插就是把新的結點插在原來的節點和原來的節點的下一個節點之間的節點。
第一步:獲取當前節點的位置,也就是訪問頭節點
struct list * p=header;
第二步:新的節點的下一個節點設置爲原來 頭節點的下一個節點(先讓要插入的指向後面的節點)
new ->next=p->next;
第三步:原來的頭節點的下一個節點設置爲新插入的頭節點。
4.單鏈表的遍歷:
我們知道一條單向鏈表是由頭節點和若干個節點組成,最後一個節點爲NULL。
思考:
1.我們需要打印頭節點嗎?(頭節點不用打印,因爲我們是爲了操作方便而設置的一個節點)
2.這條鏈表有多少個節點我怎麼知道?(通過判斷該節點是否爲尾節點,因爲尾節點指向NULL)
那麼我們現在就可以遍歷我們的鏈表:
第一步:獲取當前節點的位置,也就是訪問頭節點
struct list * p=header;
第二步:由於頭節點我們不需要去打印它,這時候,初始化打印的節點需要從第一個結點開始。
p=p->next;
第三步:判斷是否爲最後一個節點,如果不是,就先打印第一個節點的數據(1),然後移動到下一個節點(2),重複這兩個步驟。
1.while(NULL!=p-next){printf("node:%d\n",p->data);p=p->next;}
printf(“node:%d\n”,p->data);//打印最後一個指向null的數據域;
2.也可以一步打印:while(NULL!=p>next){p=p->next;printf("node:%d\n",p->data);}
5、單鏈表的刪除
刪除的原型可以定義爲:
int * detele_list_node(struct list* pH,int data);//指針、位置、數據
單鏈表的刪除要考慮兩種情況:
一種的普通節點的刪除(當然,頭節點不能算)即:改變指向,釋放內存!
還有一種是尾節點的前一個節點刪除的情況,注意,刪除完節點還要釋放對應節點的內存空間。即:指向NULL,釋放空間。
設計流程:
第一步:先定義兩個指針,一個表示當前的節點,另一個表示當前節點的下一個節點。
struct list *p=header;//當前節點
struct list *prev=NULL;//當前節點的上一個節點
第二步:遍歷整個鏈表,同時保存當前節點的前一個節點
while(NULL!=p->next)
{
//保存了當前節點的前一個節點
prev=p;
//保存當前偏移的節點
p=p->next;
return 0;
}
第三步:在遍歷的過程中查找要刪除的數據
while(NULL != p->next)
{
//保存了當前的節點的前一個節點
prev = p ;
//保存當前偏移的節點
p = p->next ;
//查找到了數據
if(p->id == data)
{
}
return 0 ;
}
第四步:查找到數據分兩種情況:
普通節點的刪除
if(p->id == data)
{
prev->next = p->next ;
free(p);
}
考慮尾節點的下一個節點爲NULL的節點刪除
if(p->id == data)
{
if(p->next == NULL)
{
prev->next = NULL ;
free(p);
}
}
單鏈表的逆序:
逆序步驟:
第一步:設置兩個指針,一個保存當前的第一個節點的位置,一個表示鏈表的第一個有效節點
//保存當前第一個節點的位置
struct list *p=pH->next;
//表示鏈表的第一個有效節點
struct list *pBack;
第二步:當沒有有效節點或者中有一個有效節點的時候,不做任何操作。
if(p==NULL||p->next==NULL)
return ;
第三步:鏈表遍歷
1.保存第一個節點的下一個節點的地址
pBock=p->next;
2.對第一個有效節點做特殊處理,也就是把第一個有效節點放到末尾去。
if(p==pH->next)p->next=NULL;
3.如果不是第一個有效點,則進行頭插操作,把當前的節點當作一個新的節點:
p->next=pH->next;//尾部連接
pH->next=p//頭部連接
4.繼續走下一個節點
p=pBack;
第四步:手動插入最後一個節點
top_insret(pH,p);
單鏈表的基本流程就搞懂了,下面寫一個程序!!!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct slist
{
int id ;
struct slist *next ;
}L;
//創建一個節點
L *create_node(int data)
{
//給每個節點分配結構體一樣的空間大小
L *p = malloc(sizeof(L));
if(NULL == p)
{
printf("malloc error!\n");
return NULL ;
}
//由於結構體在未初始化的時候一樣是髒數據,所以要清
memset(p,0,sizeof(L));
//初始化第一個節點
p->id = data ;
//將節點的後繼指針設置爲NULL
p->next = NULL ;
}
//鏈表的尾插
void tail_insert(L *pH , L *new)
{
//獲取當前的位置
L *p = pH ;
//如果當前位置的下一個節點不爲空
while(NULL != p->next)
{
//移動到下一個節點
p = p->next ;
}
//如果跳出以上循環,所以已經到了NULL的這個位置
//此時直接把新插入的節點賦值給NULL這個位置
p->next = new ;
}
//鏈表的頭插
void top_insert(L *pH , L *new)
{
L *p = pH ;
new->next = p->next ;
p->next = new ;
}
//鏈表的遍歷
void Print_node(L *pH)
{
//獲取當前的位置
L *p = pH ;
//獲取第一個節點的位置
p = p->next ;
//如果當前位置的下一個節點不爲空
while(NULL != p->next)
{
//(1)打印節點的數據
printf("id:%d\n",p->id);
//(2)移動到下一個節點,如果條件仍爲真,則重複(1),再(2)
p = p->next ;
}
//如果當前位置的下一個節點爲空,則打印數據
//說明只有一個節點
printf("id:%d\n",p->id);
}
//刪除鏈表中的節點
int detele_list_node(L * pH , int data)
{
//獲取當前頭節點的位置
L *p = pH ;
L *prev = NULL;
while(NULL != p->next)
{
//保存當前節點的前一個節點的指針
prev = p ;
//然後讓當前的指針繼續往後移動
p = p->next ;
//判斷,找到了要刪除的數據
if(p->id == data)
{
//兩種情況,一種是普通節點,還有一種是尾節點
if(p->next != NULL) //普通節點的情況
{
prev->next = p->next ;
free(p);
}
else //尾節點的情況
{
prev->next = NULL ; //將這個尾節點的上一個節點的指針域指向空
free(p);
}
return 0 ;
}
}
printf("沒有要刪除的節點\n");
return -1 ;
}
void trave_list(L * pH)
{
//保存第一個節點的位置
L *p = pH->next;
L *pBack;
int i = 0 ;
if(p->next == NULL || p == NULL)
return ;
while(NULL != p->next) //遍歷鏈表
{
//保存第一個節點的下一個節點
pBack = p->next ;
//找到第一個有效節點,其實就是頭指針的下一個節點
if(p == pH->next)
{
//第一個有效節點就是最後一個節點,所以要指向NULL
p->next = NULL ;
}
else
{
/*
new->next = p->next ;
p->next = new ;
*/
p->next = pH->next ; //尾部連接
}
pH->next = p ; //頭部連接
p = pBack ; //走下一個節點
}
top_insert(pH,p); //插入最後一個節點
}
int main(int argc , char **argv)
{
//創建第一個節點
int i ;
L *header = create_node(0);
for(i = 1 ; i < 10 ; i++)
{
tail_insert(header,create_node(i));
}
Print_node(header);
detele_list_node(header,5);
putchar('\n');
Print_node(header);
putchar('\n');
trave_list(header);
Print_node(header);
return 0 ;
}
相關參考連接:
https://blog.csdn.net/morixinguan/article/details/68951912