www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]為什么要學(xué)習(xí)鏈表? 鏈表主要有以下幾大特性: 1、解決數(shù)組無(wú)法存儲(chǔ)多種數(shù)據(jù)類型的問(wèn)題。 2、解決數(shù)組中,元素個(gè)數(shù)無(wú)法改變的限制(C99的變長(zhǎng)數(shù)組,C++也有變長(zhǎng)數(shù)組可以實(shí)現(xiàn))。 3、數(shù)組移動(dòng)元素的過(guò)程中,要對(duì)元素進(jìn)行大范圍的移動(dòng),很耗時(shí)間,效率也不高。 先

為什么要學(xué)習(xí)鏈表?


鏈表主要有以下幾大特性:


1、解決數(shù)組無(wú)法存儲(chǔ)多種數(shù)據(jù)類型的問(wèn)題。


2、解決數(shù)組中,元素個(gè)數(shù)無(wú)法改變的限制(C99的變長(zhǎng)數(shù)組,C++也有變長(zhǎng)數(shù)組可以實(shí)現(xiàn))。


3、數(shù)組移動(dòng)元素的過(guò)程中,要對(duì)元素進(jìn)行大范圍的移動(dòng),很耗時(shí)間,效率也不高。


先來(lái)感性的認(rèn)識(shí)一下鏈表,我們先來(lái)認(rèn)識(shí)下簡(jiǎn)單的鏈表:

從這幅圖我們得出以下信息:


這個(gè)簡(jiǎn)單鏈表的構(gòu)成:


頭指針(Header),若干個(gè)節(jié)點(diǎn)(節(jié)點(diǎn)包括了數(shù)據(jù)域和指針域),最后一個(gè)節(jié)點(diǎn)要指向空。


實(shí)現(xiàn)原理:頭指針指向鏈表的第一個(gè)節(jié)點(diǎn),然后第一個(gè)節(jié)點(diǎn)中的指針指向下一個(gè)節(jié)點(diǎn),然后依次指到最后一個(gè)節(jié)點(diǎn),這樣就構(gòu)成了一條鏈表。


接下來(lái)看看鏈表的數(shù)據(jù)結(jié)構(gòu):

struct  list_node

{

int data ; //數(shù)據(jù)域,用于存儲(chǔ)數(shù)據(jù)

struct list_node *next ; //指針,可以用來(lái)訪問(wèn)節(jié)點(diǎn)數(shù)據(jù),也可以遍歷,指向下一個(gè)節(jié)點(diǎn)

};

那么如何來(lái)創(chuàng)建一個(gè)鏈表的一個(gè)節(jié)點(diǎn)呢?

我們寫(xiě)個(gè)程序演示一下:

#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 ;          //1、首先,當(dāng)然是定義一個(gè)頭指針

node = (list_single *)malloc(sizeof(list_single)); //2、然后分配內(nèi)存空間

if(node == NULL){

printf("malloc fair!\n");

}

memset(node,0,sizeof(list_single)); //3、清一下

node->data = 100 ;     //4、給鏈表節(jié)點(diǎn)的數(shù)據(jù)賦值

node->next = NULL ;                 //5、將鏈表的指針域指向空

printf("%d\n",node->data);

free(node);

return 0 ;

}

    那么,這僅僅只是創(chuàng)建一個(gè)鏈表中的一個(gè)節(jié)點(diǎn),為了好看,我們把創(chuàng)建節(jié)點(diǎn)封裝成函數(shù),以后想創(chuàng)建多少個(gè)節(jié)點(diǎn),我們就可以反復(fù)調(diào)用一個(gè)函數(shù)來(lái)創(chuàng)建,會(huì)很方便:

list_single *create_list_node(int data)

{

list_single *node = NULL ;

node = (list_single *)malloc(sizeof(list_single));

if(node == NULL){

printf("malloc fair!\n");

}

memset(node,0,sizeof(list_single));

node->data = 100 ;

node->next = NULL ;

return node ;

}

接下來(lái)在程序上完成的程序:

#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 *create_list_node(int data)

{

list_single *node = NULL ;

node = (list_single *)malloc(sizeof(list_single));

if(node == NULL){

printf("malloc fair!\n");

}

memset(node,0,sizeof(list_single));

node->data = data;

node->next = NULL ;

return node ;

}

int main(void)

{

int data = 100 ;

list_single *node_ptr = create_list_node(data); //創(chuàng)建一個(gè)節(jié)點(diǎn)

printf("node_ptr->data=%d\n",node_ptr->data);   //打印節(jié)點(diǎn)里的數(shù)據(jù)

printf("node_ptr->next=%d\n",node_ptr->next);  

free(node_ptr);

return 0 ;

}

執(zhí)行結(jié)果 :

這樣我們就完成一個(gè)鏈表節(jié)點(diǎn)的創(chuàng)建了,那么它現(xiàn)在的樣子如下圖:


鏈表的結(jié)構(gòu)里,數(shù)據(jù)存儲(chǔ)了100,因?yàn)檫@個(gè)鏈表只有一個(gè)節(jié)點(diǎn),所以它的指針域指向了NULL。


上面只是建立一個(gè)單鏈表的基本雛形,接下來(lái)咱們?cè)賮?lái)增加一點(diǎn)難度。如果創(chuàng)建多個(gè)單鏈表節(jié)點(diǎn),實(shí)現(xiàn)單鏈表的增刪改查?把單鏈表應(yīng)用起來(lái)。

1、首先定義一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)


創(chuàng)建節(jié)點(diǎn)函數(shù)原型可定義如下:

struct list *create_node(int data) ;

如何創(chuàng)建單鏈表的節(jié)點(diǎn),主要分以下步驟:

(1)給當(dāng)前的每個(gè)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)配置定量的空間大小

   ep : struct list *node = malloc(sizeof(struct list));

(2)清節(jié)點(diǎn)數(shù)據(jù)(由于結(jié)構(gòu)體變量在未初始化的時(shí)候,數(shù)據(jù)是臟的)

   ep : memset(node,0,sizeof(struct list));

(3)給節(jié)點(diǎn)初始化數(shù)據(jù)

   ep : node->id = data ; 

(4)將該節(jié)點(diǎn)的指針域設(shè)置為NULL

   ep : node->next = NULL ;

2、單鏈表的尾插:

尾插節(jié)點(diǎn)函數(shù)原型可定義如下:

如何將當(dāng)前鏈表和新的節(jié)點(diǎn)相連接?只要實(shí)現(xiàn):

header->next = new 

尾插流程如下:


(1)獲取當(dāng)前節(jié)點(diǎn)的位置,也就是訪問(wèn)頭節(jié)點(diǎn)

   ep : struct list *p = header ;

(2)判斷是否為最后一個(gè)節(jié)點(diǎn),如果不是,移動(dòng)到下一個(gè)節(jié)點(diǎn),如果是,將數(shù)據(jù)插入尾部。

   ep : while(NULL != p->next) p = p->next ;

        p->next = new ;

3、單鏈表的頭插

很好理解,頭插就是把新的節(jié)點(diǎn)插在原來(lái)的節(jié)點(diǎn)和原來(lái)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)之間的一個(gè)節(jié)點(diǎn)。如圖,新的節(jié)點(diǎn)插在頭節(jié)點(diǎn)和節(jié)點(diǎn)1。

所以可以推出頭插流程如下:

(1)獲取當(dāng)前節(jié)點(diǎn)的位置,也就是訪問(wèn)頭節(jié)點(diǎn)

    ep : struct list *p = header ;

(2)新的節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為原來(lái)頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(第一個(gè)節(jié)點(diǎn))

    ep : new->next = p->next ;

(3)原來(lái)的頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置為現(xiàn)在新插入的頭節(jié)點(diǎn)

    ep : p->next = new ;

4、單鏈表的遍歷

如圖為一條單向鏈表的模型,看圖知道該鏈表由頭節(jié)點(diǎn)和若干個(gè)節(jié)點(diǎn)組成,最后一個(gè)節(jié)點(diǎn)(尾節(jié)點(diǎn))為NULL 。

從圖中可以得出信息,如果我們要打印出各個(gè)節(jié)點(diǎn)的數(shù)據(jù),要考慮以下問(wèn)題:

(1)需要打印頭節(jié)點(diǎn)嗎?(頭節(jié)點(diǎn)肯定是不用打印的,因?yàn)檫@是我們?yōu)榱瞬僮鞣奖愣O(shè)置的一個(gè)節(jié)點(diǎn))。

(2)這條鏈表有多少個(gè)節(jié)點(diǎn)我們?cè)趺粗溃?通過(guò)判斷該鏈表是否已經(jīng)到達(dá)了尾節(jié)點(diǎn),標(biāo)志就是NULL)

那么可以得到流程如下:

(1)獲取當(dāng)前節(jié)點(diǎn)的位置,也就是訪問(wèn)頭節(jié)點(diǎn)

    ep : struct list *p = header ;

(2)由于頭節(jié)點(diǎn)我們不需要去打印它,這時(shí)候,初始化打印的節(jié)點(diǎn)需要從第一個(gè)節(jié)點(diǎn)開(kāi)始。

    ep : p = p->next ;  

(3)判斷是否為最后一個(gè)節(jié)點(diǎn),如果不是,先打印第一個(gè)節(jié)點(diǎn)的數(shù)據(jù)(1),然后移動(dòng)到下一個(gè)節(jié)點(diǎn)(2),重復(fù)這兩個(gè)步驟。

   如果是最后一個(gè)節(jié)點(diǎn),直接打印數(shù)據(jù)即可。

    while(NULL != p->next){ printf("node:%d\n",p->data) ; p = p->next ;}

    printf("node:%d\n",p->data);

    當(dāng)然還可以一句代碼解決,這樣就達(dá)到了先偏移,后取數(shù)據(jù)。

    while(NULL != p->next){ p = p->next ; printf("node:%d\n",p->data) ; }

5、單鏈表的刪除

刪除節(jié)點(diǎn)的函數(shù)原型可定義如下:

int detele_list_node(struct list *pH , int data);

單向鏈表的刪除要考慮兩種情況,一種的普通節(jié)點(diǎn)的刪除(當(dāng)然,頭節(jié)點(diǎn)不能算)

還有一種是尾節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)的刪除情況,注意,刪除完節(jié)點(diǎn)還需要釋放對(duì)應(yīng)節(jié)點(diǎn)的內(nèi)存空間。

刪除節(jié)點(diǎn)的設(shè)計(jì)流程:

(1)先定義兩個(gè)指針,一個(gè)表示當(dāng)前的節(jié)點(diǎn),另一個(gè)表示當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)。

    ep : struct list *p = header ;  //當(dāng)前節(jié)點(diǎn)

         struct list *prev = NULL ; //當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)

(2)遍歷整個(gè)鏈表,同時(shí)保存當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)

    ep : while(NULL != p->next)

        { 

          //保存了當(dāng)前的節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)

          prev = p ;  

          //保存當(dāng)前偏移的節(jié)點(diǎn)

          p = p->next ; 

          return 0 ;

        }

(3)在遍歷的過(guò)程中查找要?jiǎng)h除的數(shù)據(jù)

    ep : while(NULL != p->next)

        { 

          //保存了當(dāng)前的節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)

          prev = p ;  

          //保存當(dāng)前偏移的節(jié)點(diǎn)

          p = p->next ; 

          //查找到了數(shù)據(jù)

          if(p->id == data)

          {

          

          }

          return 0 ;

        }

(4)查找到了數(shù)據(jù)后,分兩種情況刪除

    ep : 普通節(jié)點(diǎn)的刪除

        if(p->id == data)

        {

            prev->next = p->next ;

            free(p);

        }

    ep : 考慮尾節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為NULL的節(jié)點(diǎn)刪除

        if(p->id == data)

        {

            if(p->next == NULL)

            {

                prev->next = NULL ;

                free(p);

            }

        }

6、單鏈表的逆序

逆序步驟:

   單鏈表的基本操作流程咱們基本搞懂了,下面寫(xiě)一個(gè)程序,這將會(huì)變得非常非常的簡(jiǎn)單。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct slist

{

int id ;

struct slist *next ;

}L;

 

//創(chuàng)建一個(gè)節(jié)點(diǎn) 

L *create_node(int data)

{

//給每個(gè)節(jié)點(diǎn)分配結(jié)構(gòu)體一樣的空間大小 

L *p = malloc(sizeof(L));

if(NULL == p)

{

printf("malloc error!\n");

return NULL ;

}

//由于結(jié)構(gòu)體在未初始化的時(shí)候一樣是臟數(shù)據(jù),所以要清 

memset(p,0,sizeof(L));

//初始化第一個(gè)節(jié)點(diǎn) 

p->id = data ; 

//將節(jié)點(diǎn)的后繼指針設(shè)置為NULL 

p->next = NULL ;

}

 

//鏈表的尾插 

void tail_insert(L *pH , L *new)

{

//獲取當(dāng)前的位置 

L *p = pH ; 

//如果當(dāng)前位置的下一個(gè)節(jié)點(diǎn)不為空 

while(NULL != p->next)

{

//移動(dòng)到下一個(gè)節(jié)點(diǎn) 

p = p->next ;

}

//如果跳出以上循環(huán),所以已經(jīng)到了NULL的這個(gè)位置

//此時(shí)直接把新插入的節(jié)點(diǎn)賦值給NULL這個(gè)位置 

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)

{

//獲取當(dāng)前的位置 

L *p = pH ;

//獲取第一個(gè)節(jié)點(diǎn)的位置 

p = p->next ;

//如果當(dāng)前位置的下一個(gè)節(jié)點(diǎn)不為空 

while(NULL != p->next)

{

//(1)打印節(jié)點(diǎn)的數(shù)據(jù) 

printf("id:%d\n",p->id);

//(2)移動(dòng)到下一個(gè)節(jié)點(diǎn),如果條件仍為真,則重復(fù)(1),再(2) 

p = p->next ;

}

//如果當(dāng)前位置的下一個(gè)節(jié)點(diǎn)為空,則打印數(shù)據(jù)

//說(shuō)明只有一個(gè)節(jié)點(diǎn) 

printf("id:%d\n",p->id);

}

 

//刪除鏈表中的節(jié)點(diǎn) 

int detele_list_node(L * pH , int data)

{

//獲取當(dāng)前頭節(jié)點(diǎn)的位置 

L *p = pH ;

L *prev = NULL;

while(NULL != p->next)

{

//保存當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)的指針 

prev = p ;

//然后讓當(dāng)前的指針繼續(xù)往后移動(dòng) 

p = p->next ;

//判斷,找到了要?jiǎng)h除的數(shù)據(jù)  

if(p->id == data)

{

//兩種情況,一種是普通節(jié)點(diǎn),還有一種是尾節(jié)點(diǎn)

if(p->next != NULL)  //普通節(jié)點(diǎn)的情況 

{

prev->next = p->next ;

free(p);

}

else //尾節(jié)點(diǎn)的情況 

{

prev->next = NULL ; //將這個(gè)尾節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)的指針域指向空 

free(p); 

}

return 0  ;

}

}

printf("沒(méi)有要?jiǎng)h除的節(jié)點(diǎn)\n");

return -1 ;

}

 

void trave_list(L * pH)

{

//保存第一個(gè)節(jié)點(diǎn)的位置 

L *p = pH->next;

L *pBack;

int i = 0 ;

if(p->next == NULL || p == NULL)

return ;

while(NULL != p->next) //遍歷鏈表 

{

//保存第一個(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn) 

pBack = p->next ; 

//找到第一個(gè)有效節(jié)點(diǎn),其實(shí)就是頭指針的下一個(gè)節(jié)點(diǎn) 

if(p == pH->next) 

{

//第一個(gè)有效節(jié)點(diǎn)就是最后一個(gè)節(jié)點(diǎn),所以要指向NULL 

p->next = NULL ; 

else

{

/*

new->next = p->next ;

p->next = new ;

*/

p->next = pH->next ; //尾部連接 

}

pH->next = p ; //頭部連接 

p = pBack ; //走下一個(gè)節(jié)點(diǎn) 

}

top_insert(pH,p); //插入最后一個(gè)節(jié)點(diǎn) 

}

 

int main(int argc , char **argv) 

{

//創(chuàng)建第一個(gè)節(jié)點(diǎn) 

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 ;

}

運(yùn)行結(jié)果:


當(dāng)然,單鏈表存在一定的弊端,就是查找數(shù)據(jù)和刪除數(shù)據(jù)的時(shí)候比較麻煩,而雙鏈表的出現(xiàn)就是為了解決它的弊端:

雙鏈表的引入是為了解決單鏈表的不足:

(1)雙鏈表可以往前遍歷,也可以往后遍歷,具有兩個(gè)方向

雙鏈表的節(jié)點(diǎn) = 有效數(shù)據(jù) + 兩個(gè)指針(分別指向前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn))

雙向鏈表的圖形結(jié)構(gòu)描述:

struct double_list                                   struct double_list

{                                                            {

    數(shù)據(jù)域;                  ep :------->                   int data ;

    指針域(前向指針) ;                                   struct double_list *prev ;

    指針域(后向指針) ;                                   struct double_list *next ;

};                                                             };

1、雙向鏈表的創(chuàng)建

struct list *create_node(int data) ;

創(chuàng)建步驟(與單鏈表類似,就是多了一個(gè)指針):

(1)給節(jié)點(diǎn)申請(qǐng)空間:

   ep : struct double_list *p = malloc(sizeof(struct double_list));

(2)初始化數(shù)據(jù)域

   ep : p->data = data ;

(3)初始化指針域

   ep : p->prev = NULL ; 

        p->next = NULL ;

2、雙向鏈表的尾插

雙向鏈表尾插節(jié)點(diǎn)的函數(shù)可以定義如下:

void double_list_tail_insert(struct double_list *header , struct double_list *new) ;

尾插圖示操作:


尾插的步驟:



(1)找到鏈表的尾節(jié)點(diǎn)

   ep : 和單鏈表差不多

        DL *p = header ;

        while(NULL != p->next)

            p = p->next ;

(2)將新的節(jié)點(diǎn)連接到尾節(jié)點(diǎn)的后面成為新的節(jié)點(diǎn)

   1.原來(lái)的next指針指向新節(jié)點(diǎn)的首地址。            p->next = new ;

   2.新節(jié)點(diǎn)的prev指針指向原來(lái)的尾節(jié)點(diǎn)的首地址。 new->prev = p;

 

3、雙向鏈表的頭插

雙向鏈表頭插節(jié)點(diǎn)的函數(shù)可以定義如下:

void double_list_top_insert(struct double_list *header , struct double_list *new) ;

4、雙向鏈表的遍歷

4.1 正向遍歷

    void double_list_for_each(DL *header)

    步驟:和單鏈表完全一致,沒(méi)什么好寫(xiě)的。

    

    

4.2 反向遍歷

    void double_list_for_each_nx(DL *header)

    步驟:(1)和單鏈表一樣,先循環(huán)找到最后一個(gè)節(jié)點(diǎn)的地址

          (2)再依靠prev指針循環(huán)往前移動(dòng)

             2.1 先打印最后一個(gè)數(shù)據(jù)  ep : printf("%d ",p->data);

             2.2 向前循環(huán)遍歷         ep : p = p->prev ;

             

             判斷條件:header->prev != p->prev,

             header保存的是頭節(jié)點(diǎn)的地址,

             header->prev保存的是頭節(jié)點(diǎn)的prev的地址,

             header->next保存的是頭節(jié)點(diǎn)的next的地址,

             頭節(jié)點(diǎn)在創(chuàng)建的時(shí)候:

             header->prev = NULL ;

             header->next = NULL ;

             所以這個(gè)條件這樣寫(xiě)header->prev = NULL也是對(duì)的。

5、雙向鏈表節(jié)點(diǎn)的刪除

假設(shè)需要?jiǎng)h除節(jié)點(diǎn)1:    

    首先:

    (1)獲取當(dāng)前節(jié)點(diǎn)的地址: 

        ep : p = header;

    (2)遍歷所有的節(jié)點(diǎn),找到要?jiǎng)h除的節(jié)點(diǎn):

        ep : 

        while(NULL != p->next)

        {

            p = p->next ;

            if(p->data == data)

            {

            

            }

        }

    (3)找到要?jiǎng)h除的數(shù)據(jù)以后,需要做判斷,判斷兩種情況,這和單鏈表差不多

    3.1 如果找到當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為空

    3.1.1    

        那就把當(dāng)前節(jié)點(diǎn)的prev節(jié)點(diǎn)指向要?jiǎng)h除的這個(gè)節(jié)點(diǎn)的prev

        因?yàn)楫?dāng)前的prev節(jié)點(diǎn)保存的是要?jiǎng)h除的上一個(gè)節(jié)點(diǎn)的指針 

        p->next->prev = p->prev ;

    3.1.2    

        然后將當(dāng)前節(jié)點(diǎn)的prev指針(也就是上一個(gè)節(jié)點(diǎn)的指針)指向當(dāng)前節(jié)點(diǎn)(要?jiǎng)h除的)的下一個(gè)節(jié)點(diǎn):

        p->prev->next = p->next ;

    3.1.3    

        最后釋放刪除指針的空間:

        free(p);

        

    3.2 如果找到當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為空

    3.2.1   

    直接把當(dāng)前指針(要?jiǎng)h除的節(jié)點(diǎn))的prev指針(保存著當(dāng)前指針的上一個(gè)節(jié)點(diǎn)的地址)的下一個(gè)next指針設(shè)置為空。

    p->prev->next = NULL ;

    3.2.2

    將刪除的指針的空間釋放:

    free(p);

    看來(lái),雙鏈表學(xué)起來(lái)比單鏈表容易多了!確實(shí)啊,多了一個(gè)方向,操作起來(lái)就更加容易了,但是多了一個(gè)方向,一維多了一個(gè)指針,相比增加了一定的復(fù)雜度,但是,只要牢記prev指針和next指針的指向,那么,手畫(huà)一圖,代碼即可寫(xiě)出!

下面給一個(gè)案例實(shí)現(xiàn)一下雙向鏈表:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

//創(chuàng)建一個(gè)雙鏈表的數(shù)據(jù)結(jié)構(gòu)

typedef struct __double_list

{

int data ;

struct __double_list *prev ;

struct __double_list *next ;

}DL ; 

 

//創(chuàng)建雙向鏈表并插入一個(gè)節(jié)點(diǎn) 

DL *create_dl_node(int data)

{

DL *p = malloc(sizeof(DL));

if(NULL == p)

{

printf("create dl node fair!\n");

return NULL ;

}

//初始化數(shù)據(jù) 

p->data = data ;

//初始化指針 

p->next = NULL ;

p->prev = NULL ;

}

 

//雙向鏈表的尾插 

void double_list_tail_insert(DL *header , DL *new)

{

//取得當(dāng)前節(jié)點(diǎn)的地址 

DL *p = header ;

//找到鏈表的尾節(jié)點(diǎn) 

while(NULL != p->next)

{

//找不到接著找 

p = p->next ;

}

//找到了尾節(jié)點(diǎn),指向新節(jié)點(diǎn)的首地址 

p->next = new ;

//新節(jié)點(diǎn)的prev指針指向原來(lái)的尾節(jié)點(diǎn)的首地址。

new->prev = p;

}

 

//雙向鏈表的頭插(也就是插在兩個(gè)節(jié)點(diǎn)之間的插入方式)

void double_list_top_insert(DL *header , DL *new)

{

//新節(jié)點(diǎn)的next指針指向原來(lái)的節(jié)點(diǎn)一的地址

new->next = header->next ; 

//判斷當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)的地址是否為空

if(NULL != header->next) 

header->next->prev = new ; //節(jié)點(diǎn)1的prev指針指向新節(jié)點(diǎn)的地址 

header->next = new ;

new->prev = header ;

}

 

//雙向鏈表的正向遍歷 

void double_list_for_each(DL *header)

{

DL *p = header ;

while(NULL != p->next)

{

p = p->next ;

printf("%d ",p->data);

}

}

 

//雙向鏈表的反向遍歷 

void double_list_for_each_nx(DL *header)

{

DL *p = header ;

//先找到尾節(jié)點(diǎn)

while(NULL != p->next)

{

p = p->next ;

//已經(jīng)找到了尾節(jié)點(diǎn),向前遍歷,注意,遍歷到頭節(jié)點(diǎn)之前

//限制條件: != 頭結(jié)點(diǎn) 

while(NULL != p->prev)

{

printf("%d ",p->data);

p = p->prev ;

}

}

 

//雙向鏈表節(jié)點(diǎn)的刪除

int double_list_delete_node(DL *header , int data)

{

//取得當(dāng)前節(jié)點(diǎn) 

DL *p = header;

//遍歷所有的節(jié)點(diǎn) 

while(NULL != p->next)

{

p = p->next ;

//找到了對(duì)應(yīng)要?jiǎng)h除的數(shù)據(jù)了 

if(p->data == data)

{

//一樣存在兩種情況

//(1)當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為空

if(p->next != NULL)

{

//那就把當(dāng)前節(jié)點(diǎn)的prev節(jié)點(diǎn)指向要?jiǎng)h除的這個(gè)節(jié)點(diǎn)的prev

//因?yàn)楫?dāng)前的prev節(jié)點(diǎn)保存的是要?jiǎng)h除的上一個(gè)節(jié)點(diǎn)的指針 

p->next->prev = p->prev ;

//還要指定它的next節(jié)點(diǎn) 

p->prev->next = p->next ;

free(p);

}

//(2)當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為空 

else

{

//把 

p->prev->next = NULL ;

free(p); 

}

return 0 ;

}

}

printf("\n沒(méi)有找到對(duì)應(yīng)要?jiǎng)h除的節(jié)點(diǎn),或者節(jié)點(diǎn)已經(jīng)被刪除!\n");

return -1 ;

 

int main(void)

{

int i ;

DL *header = create_dl_node(0);

for(i = 0 ; i < 10 ; i++)

{

//雙向鏈表的尾插 

//double_list_tail_insert(header,create_dl_node(i));

//雙向鏈表的頭插 

double_list_top_insert(header,create_dl_node(i));

}

//雙向鏈表的正向遍歷 

printf("雙向鏈表的正向遍歷:");

double_list_delete_node(header,5);

double_list_for_each(header);

// double_list_delete_node(header,9);

double_list_delete_node(header,5);

putchar('\n');

printf("雙向鏈表的反向遍歷:");

double_list_for_each_nx(header);

return 0 ;

}

運(yùn)行結(jié)果:

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉