算法-鏈表之簡(jiǎn)單算法題

上一階段的字符串系列之后很長(zhǎng)時(shí)間都沒(méi)有更新文章咖为,現(xiàn)在接著來(lái),本階段是鏈表系列的凯旭,鏈表系列的算法題我不會(huì)再用大量篇幅寫(xiě)一個(gè)問(wèn)題的解決方案篡石,針對(duì)一個(gè)問(wèn)題寫(xiě)一個(gè)解決方案芥喇,所以篇幅會(huì)小很多。這篇文章會(huì)介紹和鏈表相關(guān)的簡(jiǎn)單算法題凰萨,后面還會(huì)介紹復(fù)雜算法題继控。

  • 添加/刪除結(jié)點(diǎn)
  • 從尾到頭打印鏈表
  • 翻轉(zhuǎn)單鏈表
  • 在O(1)時(shí)間內(nèi)刪除鏈表結(jié)點(diǎn)

1.添加/刪除結(jié)點(diǎn)

我們都知道,鏈表的添加和刪除操作相比數(shù)組是很方便的胖眷,因?yàn)殒湵聿灰蠼Y(jié)點(diǎn)的物理順序和邏輯順序相同武通,所以添加刪除結(jié)點(diǎn)的時(shí)候不需要像數(shù)組一樣移動(dòng)大量的結(jié)點(diǎn),借助指針珊搀,修改指針指向冶忱,我們就可以很方便的實(shí)現(xiàn)添加和刪除結(jié)點(diǎn)的操作。

添加/刪除結(jié)點(diǎn)是鏈表類算法題中比較簡(jiǎn)單的操作了境析,不會(huì)有太大問(wèn)題囚枪。主要是在注意細(xì)節(jié),添加/刪除的時(shí)候要注意判斷鏈表是否為空劳淆,如果鏈表為空链沼,就要修改頭指針的指向,也就是修改頭指針指向的地址沛鸵。

代碼

鏈表的數(shù)據(jù)結(jié)構(gòu)的定義:
注意:這些聲明中包含了一個(gè)自引用結(jié)構(gòu)的列子括勺。在定義結(jié)構(gòu)ListNode之前,已經(jīng)定義了指向該結(jié)構(gòu)的指針曲掰。C語(yǔ)言允許定義指向尚不存在的類型的指針疾捍。

typedef struct ListNode *list_pointer;
typedef struct ListNode
{
    int value;
    list_pointer link;
};
list_pointer pHead;

在鏈表尾添加節(jié)點(diǎn)
有在前端,中間插入結(jié)點(diǎn)的栏妖,也有在尾部乱豆,這里的例子是在鏈表尾插入結(jié)點(diǎn)。注意吊趾,如果鏈表為空咙鞍,那么添加結(jié)點(diǎn)需要修改頭指針的指向房官,所以這里接收的參數(shù)是頭指針的地址。

//pHead是指向指針的指針  ListNode** p
void addToTail(list_pointer *pHead, int value){

     list_pointer node = (list_pointer)malloc(sizeof(ListNode));
     if (node == NULL)
     {
        fprintf(stderr, "Faile\n");
        exit(1);
     }
    node->value = value;
    node->link = NULL;

    if (*pHead == NULL)
    {
    *pHead = node;
    }
    else{
        list_pointer p = *pHead;
        while (p->link != NULL){
            p = p->link;
        }
        p->link = node;
   }
}

刪除中間結(jié)點(diǎn):
刪除指定值的結(jié)點(diǎn)续滋,我們不知道該結(jié)點(diǎn)在什么位置翰守,所以需要遍歷鏈表找到結(jié)點(diǎn)。

//如果刪除首節(jié)點(diǎn)疲酌,那么需要改變首節(jié)點(diǎn)指針的指向
bool deleteNode(list_pointer *pHead, int value){
    if (*pHead == NULL)
    {
        fprintf(stderr, "The linklist is empty!\n");
        exit(1);
     }
    ListNode *node = NULL;
    if ((*pHead)->value == value){//刪除首節(jié)點(diǎn)
        node = *pHead;
        *pHead = (*pHead)->link;
        free(node);
        return true;
    }
    else{
        list_pointer p = *pHead;
        while (p->link != NULL && p->link->value != value){
            p = p->link;
        }
        if (p->link != NULL && p->link->value == value)
        {
            node = p->link;
            p->link = p->link->link;
            free(node);
        }
    }

}

2.從尾到頭打印鏈表

看到這到道題的第一反應(yīng)是從頭到尾打印鏈表會(huì)比較簡(jiǎn)單蜡峰,所以我們可以改變鏈表的指針指向,但是這樣會(huì)改變鏈表原來(lái)的結(jié)構(gòu)朗恳,是否允許改變鏈表的結(jié)構(gòu)湿颅,這個(gè)取決于面試官。這里的例子是不改變鏈表結(jié)構(gòu)粥诫。

算法思想

從尾到頭打印鏈表也就是說(shuō)先存入的元素后輸出油航,后存入的先輸出,和椈辰“后進(jìn)先出”的思想一樣谊囚,所以我們可以在遍歷鏈表的時(shí)候先將元素存入棧中,再循環(huán)遍歷棧輸出元素执赡。
想到了使用棧镰踏,而遞歸的本質(zhì)就是使用棧存儲(chǔ),那么我們使用遞歸也能實(shí)現(xiàn)同樣的效果沙合,下面的例子就是用遞歸實(shí)現(xiàn)的奠伪。
代碼:

void PrintListReversingly(list_pointer pHead) {
    list_pointer p = pHead;
    if (p) {
        if (p->link) {
            PrintListReversingly(p->link);
        }
        printf("鏈表元素:%d\n", p->value);      
    }
}

使用遞歸代碼會(huì)簡(jiǎn)潔很多,但是當(dāng)鏈表非常長(zhǎng)的時(shí)候首懈,函數(shù)調(diào)用的層級(jí)會(huì)很深绊率,可能會(huì)導(dǎo)致函數(shù)調(diào)用棧溢出,顯式的使用椌柯模基于循環(huán)來(lái)遍歷的魯棒性會(huì)好一些滤否。

3.翻轉(zhuǎn)單鏈表

題目:寫(xiě)一個(gè)函數(shù),輸入鏈表的頭結(jié)點(diǎn)挎袜,翻轉(zhuǎn)該鏈表,并返回翻轉(zhuǎn)后的鏈表的頭結(jié)點(diǎn)肥惭。

算法思想

翻轉(zhuǎn)鏈表

看上面的圖盯仪,如果是對(duì)結(jié)點(diǎn)i翻轉(zhuǎn)鏈表,就是改變i的link的指向蜜葱,將它指向前一個(gè)結(jié)點(diǎn)h全景,但是這樣會(huì)導(dǎo)致i指向j的鏈丟失,所以我們需要存儲(chǔ)下這個(gè)值牵囤,也就是說(shuō)爸黄,在一次改變指針指向的操作中滞伟,我們需要存儲(chǔ)下前一個(gè)結(jié)點(diǎn)h,后一個(gè)結(jié)點(diǎn)j炕贵,和當(dāng)前結(jié)點(diǎn)i梆奈。此外,我們還需要明確称开,在翻轉(zhuǎn)了鏈表之后亩钟,原來(lái)的頭結(jié)點(diǎn)變成了尾結(jié)點(diǎn),而現(xiàn)在的尾節(jié)點(diǎn)呢鳖轰,就是NULL清酥。分析完這些之后很容易寫(xiě)出代碼。

代碼

//翻轉(zhuǎn)鏈表
list_pointer Invert(list_pointer pHead)
{
    list_pointer middle, trail;
    middle = NULL;
    //當(dāng)pHead指向原鏈表的最后一個(gè)結(jié)點(diǎn)的link時(shí)蕴侣,退出循環(huán)
    //此時(shí)middle剛好指向原鏈表的最后一個(gè)結(jié)點(diǎn)
    while (pHead) {
        trail = middle;
        middle = pHead;//此時(shí)middle和pHead指向的地址相同
        pHead = pHead->link;
        middle->link = trail;
    }
    return middle;
}

4.在O(1)時(shí)間內(nèi)刪除鏈表結(jié)點(diǎn)

常規(guī)的思維焰轻,刪除鏈表結(jié)點(diǎn)需要知道它的前一個(gè)結(jié)點(diǎn),簡(jiǎn)單的三句代碼昆雀,就是判斷p->link->value == value辱志,然后修改p->link = p->link->link,釋放p->link.這樣就需要遍歷鏈表忆肾,知道要?jiǎng)h除的結(jié)點(diǎn)的前一個(gè)荸频,時(shí)間復(fù)雜度為O(n)。如果換一種思路呢客冈?

算法思想

假設(shè)結(jié)點(diǎn)h,i,j是鏈表中相鄰的3個(gè)結(jié)點(diǎn)旭从,現(xiàn)在要?jiǎng)h除i,可以進(jìn)行下面3步:

  1. 將結(jié)點(diǎn)j的值復(fù)制到結(jié)點(diǎn)i上场仲;
  2. 修改i的link和悦;
  3. 釋放結(jié)點(diǎn)j。
    現(xiàn)在需要考慮一下特殊情況下是否也滿足渠缕。當(dāng)刪除的節(jié)點(diǎn)是頭結(jié)點(diǎn)時(shí)鸽素,需要修改頭結(jié)點(diǎn)的link指針。當(dāng)刪除的是尾結(jié)點(diǎn)時(shí)亦鳞,它沒(méi)有下一個(gè)結(jié)點(diǎn)馍忽,所以需要遍歷鏈表,得到它的前一個(gè)結(jié)點(diǎn)燕差。當(dāng)鏈表只有一個(gè)結(jié)點(diǎn)時(shí)遭笋,刪除的結(jié)點(diǎn)是頭結(jié)點(diǎn)(尾節(jié)點(diǎn)),需要將頭指針的指向置為NULL徒探。

代碼

//在O(1)內(nèi)刪除一個(gè)結(jié)點(diǎn)
void DeleteNode(list_pointer *pHead, ListNode *node)
{
    if (!(*pHead) || !node)
        return;

    //要?jiǎng)h除的不是尾結(jié)點(diǎn)
    if (node->link)
    {
        ListNode *pNext = node->link;
        node->value = pNext->value;
        node->link = pNext->link;
        free(pNext);
    }
    //鏈表中只有一個(gè)結(jié)點(diǎn)刪除頭結(jié)點(diǎn)瓦呼,也是尾結(jié)點(diǎn)
    else if (*pHead == node)//node->link為NULL
    {
        free(node);
        *pHead = NULL;
    }
    else//鏈表中有多個(gè)結(jié)點(diǎn),刪除的是尾結(jié)點(diǎn)
    {
        list_pointer p = *pHead;
        while (p->link != node)
        {
            p = p->link;
        }
        p->link = NULL;
        free(node);
    }
}

總結(jié)

這些題難度都不大测暗,使用指針很靈活央串,但是也很容易出錯(cuò)磨澡,主要是關(guān)注細(xì)節(jié)。這篇就到這里了质和,不足之處稳摄,還請(qǐng)多指教~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市侦另,隨后出現(xiàn)的幾起案子秩命,更是在濱河造成了極大的恐慌,老刑警劉巖褒傅,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弃锐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡殿托,警方通過(guò)查閱死者的電腦和手機(jī)霹菊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)支竹,“玉大人旋廷,你說(shuō)我怎么就攤上這事±窀椋” “怎么了饶碘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)馒吴。 經(jīng)常有香客問(wèn)我扎运,道長(zhǎng),這世上最難降的妖魔是什么饮戳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任豪治,我火速辦了婚禮,結(jié)果婚禮上扯罐,老公的妹妹穿的比我還像新娘负拟。我一直安慰自己,他們只是感情好歹河,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布掩浙。 她就那樣靜靜地躺著,像睡著了一般秸歧。 火紅的嫁衣襯著肌膚如雪厨姚。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天寥茫,我揣著相機(jī)與錄音遣蚀,去河邊找鬼矾麻。 笑死纱耻,一個(gè)胖子當(dāng)著我的面吹牛芭梯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弄喘,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼玖喘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蘑志?” 一聲冷哼從身側(cè)響起累奈,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎急但,沒(méi)想到半個(gè)月后澎媒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡波桩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年戒努,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镐躲。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡储玫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萤皂,到底是詐尸還是另有隱情撒穷,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布裆熙,位于F島的核電站端礼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏弛车。R本人自食惡果不足惜齐媒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纷跛。 院中可真熱鬧喻括,春花似錦、人聲如沸贫奠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)唤崭。三九已至拷恨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谢肾,已是汗流浹背腕侄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冕杠。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓微姊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親分预。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兢交,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

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