《數(shù)據(jù)結(jié)構(gòu)》第十篇黑忱、線性表中的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)--雙鏈表

死亡飛車.jpeg

第8篇文章中我們介紹了下鏈?zhǔn)酱鎯?chǔ)中鏈表中的一種--單鏈表,但是單鏈表有一個(gè)缺點(diǎn)勒魔,就是無(wú)法快速訪問(wèn)到前驅(qū)結(jié)點(diǎn)甫煞,當(dāng)查找到某個(gè)元素時(shí),如果想找前邊的元素結(jié)點(diǎn)冠绢,需要再次從頭開始遍歷抚吠,這樣就比較麻煩。

那么就有人會(huì)問(wèn)弟胀,是否可以在結(jié)點(diǎn)中再增加一個(gè)指針來(lái)指向前驅(qū)結(jié)點(diǎn)呢楷力?答案是肯定的,增加了指向前驅(qū)結(jié)點(diǎn)的指針的鏈表稱為

雙鏈表

什么是雙鏈表孵户?

雙鏈表萧朝,顧名思義就是可以向兩個(gè)方向走的鏈表。它的每一個(gè)結(jié)點(diǎn)都有兩個(gè)指針夏哭,一個(gè)指向后繼結(jié)點(diǎn)检柬,另一個(gè)指向前驅(qū)結(jié)點(diǎn),如下圖所示


雙鏈表.png

我們從上圖中可以看到竖配,第一個(gè)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)pre指向了NULL,當(dāng)然在設(shè)計(jì)時(shí)我們也可以把它指向頭結(jié)點(diǎn)何址,最后一個(gè)結(jié)點(diǎn)的后繼結(jié)點(diǎn)next指向了NULL,這種和單鏈表是一樣的进胯。

在雙鏈表中用爪,通過(guò)一個(gè)結(jié)點(diǎn)可以找到他的后繼結(jié)點(diǎn),也就可以找到他的前驅(qū)結(jié)點(diǎn)龄减。

雙鏈表在結(jié)構(gòu)和算法描述上和單鏈表相同项钮,只是某些算法實(shí)現(xiàn)比單鏈表復(fù)雜一些班眯。例如在插入元素時(shí)希停,需要同時(shí)修改兩個(gè)方向的指針指向烁巫,根據(jù)上圖所示雙鏈表,我們?cè)谠?0和67中間插入新元素88宠能,來(lái)看一下圖示過(guò)程

插入步驟1.png
雙鏈表插入元素步驟2.png

從上面的步驟圖中亚隙,我們看出,在插入元素時(shí)违崇,需要同時(shí)修改兩個(gè)方向的指針指向阿弃,同理,在刪除元素時(shí)羞延,也要修改這兩個(gè)指針渣淳。這里就不畫圖展示了,大家“腦補(bǔ)一下就可以了~~~”


前面已經(jīng)學(xué)過(guò)了單鏈表的實(shí)現(xiàn)過(guò)程伴箩,雙鏈表的實(shí)現(xiàn)與單鏈表的實(shí)現(xiàn)大同小異入愧,接下來(lái)我們看看雙鏈表的實(shí)現(xiàn)過(guò)程。

雙鏈表的實(shí)現(xiàn)

學(xué)習(xí)完雙鏈表的定義與基本操作之后嗤谚,我們還是用c語(yǔ)言來(lái)實(shí)現(xiàn)一個(gè)雙鏈表棺蛛,此雙鏈表所具有的功能如下:

  • 創(chuàng)建雙鏈表

  • 獲取雙鏈表的長(zhǎng)度

  • 判斷雙鏈表是否為空

  • 插入、刪除巩步、查找元素

  • 銷毀雙鏈表

  • 打印雙鏈表
    在學(xué)習(xí)完單鏈表時(shí)旁赊,我們每一種操作算法都有詳細(xì)的描述和實(shí)現(xiàn)代碼,雙鏈表與單鏈表有很多操作是相似的椅野,這里就不在詳細(xì)描述终畅,但是會(huì)給出關(guān)鍵代碼的解釋。

    dlist.h(頭文件)

    #ifndef _DLIST_H_
    #define _DLIST_H_
    
    struct Node;
    typedef struct Head * pHead;//頭結(jié)點(diǎn)類型
    typedef struct Node * pNode;//數(shù)據(jù)結(jié)點(diǎn)類型
    //定義頭結(jié)點(diǎn)
    struct Head{
            int length;
            pNode next;
    };
    //定義數(shù)據(jù)結(jié)點(diǎn)
    struct Node{
      int data;
      pNode pre;//指向前驅(qū)結(jié)點(diǎn)的指針
      pNode next;//指向后繼結(jié)點(diǎn)的指針
    }竟闪;
    
    pHead DlistCreate();//創(chuàng)建雙鏈表方法聲明
    int getLength(pHead ph);//獲取鏈表長(zhǎng)度方法聲明
    int IsEmpty(pHead ph);//判斷鏈表是否為空方法聲明
    int DlistInsert(pHead ph,int pos,int val);//在鏈表的pos位置插入val元素方法聲明
    pNode DlistDelete(pHead ph,int val);//刪除鏈表中元素val方法聲明
    pNode DlistFind(pHead ph,int val);//查找鏈表中元素val方法聲明
    void DlistDestory(pHead ph);//銷毀鏈表方法聲明
    void printFront(pHead ph);//打印鏈表中的元素方法聲明(從頭開始打由搿)
    void printLast(pHead ph);(從尾部開始打印)
    #endif
    

dist.c(函數(shù)實(shí)現(xiàn)文件)

  #include "dlist.h"
  #include <stdio.h>
  #include <stdlib.h>
  //創(chuàng)建雙鏈表
  pHead DlistCreate(){
      pHead ph=(pHead)malloc(sizeof(struct Head));//為頭結(jié)點(diǎn)分配空間
      if(ph==NULL){
            printf("分配頭結(jié)點(diǎn)失斕绷术徊!");
            return NULL;
      }
    //創(chuàng)建好頭結(jié)點(diǎn)后,初始化頭結(jié)點(diǎn)中的數(shù)組
      ph->length=0;
      ph->next=NULL;
      return ph;
  }
   //獲取鏈表長(zhǎng)度
  int getLength(pHead ph){
        if(ph==NULL){
          printf("傳入的雙鏈表有誤鲸湃!");
        }
        return ph->length;
  }
  //判斷鏈表是否為空
  int IsEmpty(pHead ph){
      if(ph==NULL){
            printf("傳入的雙鏈表有誤赠涮!");
       }
     if(ph->length==0){
              return 1;
      }
    else{
          return 0;  
          }
  }
  //在鏈表的pos位置插入元素val
  int DlistInsert(pHead ph,int pos,int val){
        pNode pval=NULL;
        if(ph==NULL||pos<0||pos>ph->length){
          printf("插入元素時(shí),傳入?yún)?shù)有誤");
          }
        //如果參數(shù)無(wú)誤暗挑,給元素分配結(jié)點(diǎn)空間
        pval=(pNode)malloc(sizeof(struct Node));
        pval->data=val;
        //判斷在那個(gè)位置插入元素笋除,先判斷鏈表是否為空
        if(IsEmpty(ph)){
          ph->next=pval;
          pval->next=NULL;
          pval->pre=Null;
          }
        else{
              pNode pCur=ph->next;
              if(pos==0){
                    ph->next=pval;//將頭結(jié)點(diǎn)指向pval
                    pval->next=pCur;//pval的后繼指針指向pCur
                    pval->pre=NULL;//pval的前驅(qū)結(jié)點(diǎn)指向空
                    pCur->pre=pval;  //讓pCur的前驅(qū)結(jié)點(diǎn)指向pval
                }
              else{
                    for(int i=1;i<pos;i++){
                            pCur=pCur->next;//遍歷鏈表,找到要插入的位置炸裆,并將pCur指針后移   
                      }
                //循環(huán)結(jié)束垃它,此時(shí)pCur指向的就是要插入的位置
                //下方是將指針斷開,再連接的過(guò)程
                pval->next=pCur->next;
                pCur->next->pre=pval;
                pval->pre=pCur;
                pCur->next=pval;
                    }
              }
    ph->length++;
    return 1;
  }
  
  //刪除鏈表ph中的元素val
  pNode DlistDelete(pHead ph,int val){
          if(ph==NULL||ph->length==0){
            printf("參數(shù)傳入有誤!");
           }
          pNode pval=DlistFind(ph,val);//找到值所在的結(jié)點(diǎn)
          if(pval==NULL){
                  return NULL;
            }
          printf("將其刪除\n");
          pNode pRe=pval->pre;
          pNode pNext=pval->next;
          pRe->next=pNext;
          pNext->pre=pRe;
          return pval;
    }
    //查找某個(gè)元素
  pNode DlistFind(pHead ph,int val){
        if(ph==NULL){
        printf("參數(shù)傳遞有誤国拇!")
        }  
        pNode pTmp=ph->next;
        //此過(guò)程與單鏈表沒(méi)有差別            
      do{
              if(pTmp->data==val){
                    printf("有此元素洛史!\n");
                    return pTmp;
                }
              pTmp=pTmp->next;//如果上方判斷不成立,則繼續(xù)進(jìn)行下一個(gè)判斷
          }
      //循環(huán)條件是直到鏈表的結(jié)尾
      while(pTmp->next!=NULL);
      //如果上方都不成立酱吝,說(shuō)明鏈表中沒(méi)有這個(gè)元素也殖,直接返回NULL
      return NULL;
  }
   //銷毀鏈表
  void  DlistDestory(pHead ph){
          pNode pCur=ph->next;
          pNode=pTmp;
          if(ph==NULL){
          printf("參數(shù)傳入有誤!")务热;
          }
          while(pCur->next!=NULL){
              pTmp=pCur->next;
              free(pCur);//將結(jié)點(diǎn)釋放
              pCur=pTmp;
          }
          //回到初始化狀態(tài)
          ph->length=0;
          ph->next=NULL;
   }

  //打印鏈表中的元素忆嗜,從前往后打印
  void printFront(pHead ph){
        if(ph==NULL){
            printf("參數(shù)輸入有誤!");
        }  
        pNode pTmp=ph->next;
        while(pTmp->next!=NULL){
            printf("%d",pTmp->data);
            pTmp=pTmp->next;
        }
        printf("\n")
  }
  //倒序打印
void printLast(pHead ph){
     if(ph==NULL){
            printf("參數(shù)輸入有誤崎岂!");
        }  
        //現(xiàn)將指針移動(dòng)到最后的位置
        pNode pTmp=ph->next;
        while(pTmp->next!=NULL){
                pTmp=pTmp->next;
        }
        for(int i=--ph->length;i>=0;i--){
            printf("%d",pTmp->data);
            pTmp=pTmp->pre;
      }
}

ok捆毫,雙向鏈表基本的操作diamante就寫完了,接下來(lái)我們看一下測(cè)試文件
main.c

  #define _CRT_SECURE_NO_WARNINGS
  #include "dlist.h"
  #include <stdio.h>
  #include <stdlib.h>
  int main(){
          //創(chuàng)建一個(gè)雙鏈表
          pHead ph=NULL;
          ph=DlistCreate();
          //向鏈表中插入元素
          int num;
          printf("請(qǐng)輸入要插入的元素冲甘,輸入0結(jié)束:\n");
          while(1){
                     scanf("%d",&num);
                     if(num==0){
                      break;
                      DlistInsert(ph,0,num);//從頭添加元素
                      } 
           }
            printf("雙鏈表的長(zhǎng)度是%d\n",getLength(ph));
            printFront(ph);
            DlistInsert(ph,3,99);//在3位置插入新元素99
            printFront(ph);
            printLast(ph);
            //查找元素
            int val;
            printf("請(qǐng)輸入要查找的元素:\n");
            scanf("%d",&val);
            DlistFind(ph,val);
            //刪除元素
            int del;
            printf("請(qǐng)輸入要?jiǎng)h除的元素:\n");
            scanf("%d",&del);
            DlistDelete(ph,val);
            printFront(ph);

            //銷毀鏈表
            DlistDestoty(ph);
            printf("雙鏈表銷毀成功:\n 此時(shí)鏈表的長(zhǎng)度為%d\n",ph->length);

             system("pause");
              return 0;  
    }

總結(jié)

我們上方的代碼實(shí)現(xiàn)了雙鏈表的增刪改查功能幾個(gè)功能冻璃。

雙鏈表的插入陌粹、刪除操作埠通,實(shí)現(xiàn)起來(lái)比單鏈表稍微復(fù)雜一丟丟~俊性,其他操作都無(wú)較大的改動(dòng)瘫里。

雙鏈表是可以倒序遍歷的甥厦,為了測(cè)試倒序功能特笋,我們?cè)谏戏酱a中實(shí)現(xiàn)了printLast()方法久橙,實(shí)現(xiàn)從后往前打印鏈表的元素茵肃。

雙鏈表中的每個(gè)結(jié)點(diǎn)都要記錄兩個(gè)指針律适,所以空間消耗要略多一些辐烂。不過(guò)由于它良好的對(duì)稱性,是的對(duì)結(jié)點(diǎn)前后兩個(gè)結(jié)點(diǎn)操作更加靈活捂贿,也使得算法的時(shí)間效率得到了提高纠修,說(shuō)到底,就是空間換時(shí)間厂僧。

多謝關(guān)注扣草,大家一起努力~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市颜屠,隨后出現(xiàn)的幾起案子辰妙,更是在濱河造成了極大的恐慌,老刑警劉巖甫窟,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件密浑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡粗井,警方通過(guò)查閱死者的電腦和手機(jī)尔破,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門街图,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人懒构,你說(shuō)我怎么就攤上這事餐济。” “怎么了痴脾?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵颤介,是天一觀的道長(zhǎng)梳星。 經(jīng)常有香客問(wèn)我赞赖,道長(zhǎng),這世上最難降的妖魔是什么冤灾? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任前域,我火速辦了婚禮,結(jié)果婚禮上韵吨,老公的妹妹穿的比我還像新娘匿垄。我一直安慰自己,他們只是感情好归粉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布椿疗。 她就那樣靜靜地躺著,像睡著了一般糠悼。 火紅的嫁衣襯著肌膚如雪届榄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天倔喂,我揣著相機(jī)與錄音铝条,去河邊找鬼。 笑死席噩,一個(gè)胖子當(dāng)著我的面吹牛班缰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悼枢,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼埠忘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了馒索?” 一聲冷哼從身側(cè)響起给梅,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎双揪,沒(méi)想到半個(gè)月后动羽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渔期,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年运吓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渴邦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拘哨,死狀恐怖谋梭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倦青,我是刑警寧澤瓮床,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站产镐,受9級(jí)特大地震影響隘庄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜癣亚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一丑掺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧述雾,春花似錦街州、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至黍翎,卻和暖如春面徽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玩敏。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工斗忌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人旺聚。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓织阳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親砰粹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唧躲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容