算法-鏈表之簡單算法題(續(xù))

本來以為一篇就能寫完的眯娱,后來又感覺一篇多了一些驶乾,所以關于鏈表的簡單算法題有加了個續(xù)篇惰爬,和上一篇一樣,難度不會太大辜王。

  • 鏈表中倒數(shù)第k個結點
  • 合并兩個排序的鏈表
  • 找到兩個鏈表中的第一個公共結點

1.鏈表中的倒數(shù)第k個結點

問題描述:輸入一個鏈表劈狐,計算鏈表中的倒數(shù)第k個結點(從1開始計數(shù))。

算法思想

我們可以分析鏈表結點個數(shù)n和倒數(shù)第k個結點之間的關系呐馆,如果鏈表有n個結點肥缔,那么它的倒數(shù)第k個結點也就是鏈表順著數(shù)的第n-k+1個結點。
思路一:先遍歷鏈表汹来,得到結點個數(shù)n续膳,再遍歷鏈表,找到第n-k+1個結點收班。這一種思路需要兩次遍歷鏈表坟岔,時間復雜度為O(n)。
思路二:能不能一次遍歷就找到結點呢摔桦?這就是接下來要說的了社付。我們可以使用兩個指針,第一個指針從頭結點開始邻耕,向前走k-1步瘦穆,然后第二個指針指向首結點,接下來兩個指針順序后移赊豌,當?shù)谝粋€指針指向最后一個結點的時候扛或,第二個指針剛好指向倒數(shù)第k個幾點。兩個指針之間間隔為k-1碘饼。

代碼:

算法思想清楚了之后熙兔,代碼就簡單多了悲伶。寫出代碼之后,我們需要關注其魯棒性住涉,針對特殊的輸入麸锉,代碼能否完成任務。這些都是代碼中需要考慮到的舆声。
特殊輸入:
1.如果pHead為NULL花沉,那么不存在倒數(shù)第K個結點,應該返回NULL媳握。
2.k接收的無符號數(shù)碱屁,如果輸入k=0,因為k是無符號數(shù),for循環(huán)k-1次的時候,得到的將不會是-1蛾找,而是一個非常大的數(shù)字娩脾,for循環(huán)的次數(shù)會超過我們的預期。
3.如果輸入的k大于鏈表結點總數(shù)打毛,那么在for循環(huán)遍歷鏈表的時候柿赊,會出現(xiàn)指向NULL的指針。

鏈表的數(shù)據結構定義:

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

查找鏈表中的倒數(shù)第k個結點(從1開始計數(shù))幻枉。

list_pointer FindKthNodeFromEnd(list_pointer pHead, unsigned int k)
{
    //鏈表中的結點計數(shù)從1開始碰声,所以不存在倒數(shù)第0個結點
    if (pHead == NULL || k == 0) {
        return NULL;
    }
    list_pointer pAhead = pHead;
    list_pointer pBehind;

    for (int i = 0; i < k - 1; i++)
    {
        if (pAhead->link)//防止k大于鏈表結點個數(shù)的情況發(fā)生
        {
            pAhead = pAhead->link;
        }
        else
        {
            fprintf(stderr, "K is bigger than length of linklist\n");
            exit(1);
        }
    }

    pBehind = pHead;
    while (pAhead->link)//判斷pAhead是不是指向最后一個結點
    {
        pAhead = pAhead->link;
        pBehind = pBehind->link;
    }
    return pBehind;
}

相關題目

類似的題目還有很多,我們可以借助兩個指針熬甫,很快的得到結果奥邮。
題目一:求鏈表的中間結點,如果鏈表結點數(shù)為奇數(shù)罗珍,則輸出中間結點洽腺,如果鏈表結點數(shù)為偶數(shù), 則輸出中間兩個中的一個覆旱。
算法思想:定義兩個指針蘸朋,一個指針一次走一步,另一個指針一次走兩步扣唱,當走得快的指針到達鏈表尾是藕坯,走得慢的剛好指向鏈表中間結點。
題目二:判斷一個單向鏈表是否是環(huán)形鏈表噪沙。
算法思想:定義兩個指針炼彪,從首結點開始,一個指針一次走一步正歼,另一個指針一次走兩步辐马,如果走的快的指針追到了走得慢的指針,那么這個鏈表就是環(huán)形的局义,如果走的快的指針走到了鏈表的末尾都沒有追到慢的指針喜爷,那么就不是環(huán)形鏈表冗疮。

代碼

這里寫了題目一的代碼,可以參考一下檩帐。需要注意的代碼中的while的循環(huán)條件术幔,判斷了pAhead和pAhead->link,這里是判斷是否指向了尾節(jié)點湃密,如果鏈表結點數(shù)為偶數(shù)诅挑,是用pAhead==NULL來判斷是否到尾節(jié)點的;如果鏈表有奇數(shù)個結點泛源,就是用pAhead->link==NULL來判斷的拔妥。

//找中間結點
list_pointer getTheMiddleNode(list_pointer pHead)
{
    if (pHead == NULL){
        fprintf(stderr, "the linklist is empty.\n" );
        exit(1);
    }
    if (pHead->link == NULL)//如果鏈表中只有一個結點,那么中間結點就是該結點
        return pHead;
    list_pointer pAhead = pHead, pBehind = pHead;
    //如果走的快的指針沒有指向尾結點
    while (pAhead && pAhead->link)
    {
        pAhead = pAhead->link->link;
        pBehind = pBehind->link;
    }
    return pBehind;
}

2.合并兩個排序的鏈表

問題描述:輸入兩個排好序的鏈表俩由,合并兩個鏈表毒嫡,并返回合并后的鏈表的頭結點癌蚁。

算法思想

用兩個指針幻梯,一個指向a鏈表的結點,一個指向b鏈表的結點努释。都從鏈表頭開始碘梢,依次比較。針對兩個鏈表中的第一個結點伐蒂,比較大小煞躬,小的結點加到合并的之后的鏈表中,然后加入合并鏈表的那個鏈表指針后移逸邦,然后計算省下結點的的頭結點恩沛,接下來將計算出來的結點接到合并鏈表的鏈表尾,剩余的結點也是這樣處理缕减。這樣分析下來雷客,就是典型的遞歸了。使用遞歸桥狡,我們可以很快的解決這道題搅裙。

代碼

//合并兩個排好序的鏈表,用遞歸,每次都比較鏈表中剩余結點的頭結點
list_pointer Merge(list_pointer pHead1, list_pointer pHead2)
{
    if (pHead1 == NULL)
        return pHead2;
    if (pHead2 == NULL)
        return pHead1;

    list_pointer pMerge = NULL;//合并之后的鏈表的頭結點
    //每次都比較鏈表中剩余結點的頭指針
    if (pHead1->value < pHead2->value)
    {
        pMerge = pHead1;
        pMerge->link = Merge(pHead1->link, pHead2);
    }
    else
    {
        pMerge = pHead2;
        pMerge->link = Merge(pHead1, pHead2->link);//接到上一個結點后面
    }
    return pMerge;
}

3.找到兩個鏈表中的第一個公共結點

問題描述:輸入兩個鏈表裹芝,計算兩個鏈表的公共結點部逮。

算法思想

思路一:蠻力法,遍歷第一個鏈表嫂易,每遍歷一個結點時兄朋,在第二個鏈表上順序遍歷所有結點,如果第二個鏈表上有結點和第一個鏈表的結點相等怜械,那么就找到了公共結點蜈漓。這種思路的時間復雜度是O(n^2)穆桂,效率很低。
思路二:首先分析有公共結點的兩個鏈表的特點融虽,如果兩個鏈表存在公共結點享完,那么兩個鏈表都一定存在一個結點,它的link指向相同有额。單向鏈表的每個結點只能有一個指向般又,所以從第一個公共結點開始,之后它們的所有結點都是重合的巍佑,不可能再分叉茴迁。所以兩個有公共結點而部分重合的鏈表,看起來像Y萤衰。

首先堕义,可以確定,如果兩個鏈表有公共結點脆栋,那么公共結點一定是在鏈表尾部倦卖。如果從兩個鏈表的最后一個結點開始比較,遇到最后一個相等的結點就是它們的公共結點了椿争。但是鏈表要定位到最后一個結點就需要從頭遍歷怕膛,最先遍歷到的結點最后比較,最后遍歷到的結點(尾結點)第一個比較秦踪,所以我們可以使用棧褐捻。先遍歷兩個鏈表,用兩個棧來存儲每個結點椅邓。每次比較棧頂結點柠逞,直到比較到最后一個相等的結點。時間復雜度O(m+m)景馁,m,n分別是兩個鏈表的長度板壮,空間復雜度也是O(m+n)。

思路三:用棧的目的就是定位到最后一個元素裁僧,方便從后往前遍歷个束。這樣就解決了兩個鏈表的結點數(shù)不一致時,到達尾結點的時間不同聊疲。換一種思路茬底。如果提前知道鏈表長度,我們就可以知道長的鏈表比短的鏈表多幾個元素获洲,那么長的鏈表先走幾步阱表,然后兩個鏈表開始同時向后遍歷,遇到的第一個相同的結點就是其公共結點了。這種思想的時間復雜度是O(m+n)最爬,但是我們不再需要額外的空間輔助了涉馁。

代碼

思路三代碼。

//計算鏈表長度
unsigned int getLength(list_pointer p)
{
    list_pointer pNode = p;
    int length = 0;
    while (pNode)
    {
        length++;
        pNode = pNode->link;
    }
    return length;
}

//計算兩個鏈表的第一個公共結點
ListNode* FindFirstCommonNode(list_pointer pHead1, list_pointer pHead2)
{
    if (!pHead1 || !pHead2)
        return NULL;
    //得到兩個鏈表的長度
    unsigned int nLength1 = getLength(pHead1);
    unsigned int nLength2 = getLength(pHead2);

    int nLengthDif = nLength1 - nLength2;
    list_pointer pListHeadLong = pHead1;
    list_pointer pListHeadShort = pHead2;

    if (nLength1 < nLength2)
    {
        nLengthDif = nLength2 - nLength1;
        pListHeadLong = pHead2;
        pListHeadShort = pHead1;
    }

    //長的鏈表先走
    for (int i = 0; i < nLengthDif; i++)
    {
        pListHeadLong = pListHeadLong->link;
    }

    while (pListHeadLong && pListHeadShort
        && pListHeadLong != pListHeadShort)
    {
        pListHeadLong = pListHeadLong->link;
        pListHeadShort = pListHeadShort->link;
    }
    //得到第一個公共結點
    ListNode *theCommonNode = pListHeadLong;
    return theCommonNode;
}

總結

遇到這些問題的時候爱致,常規(guī)思路的解決方法我們的通常都能想到烤送,但是時空效率很低,看到這些高效的解決方法的時候糠悯,我覺得真是很奇妙帮坚,快捷方便的解決了問題。我們可以從這些方法之中學習一種思路互艾,比如說一個指針解決不了的试和,試試兩個指針,總結要解決的問題和鏈表特有的存儲結構的關系等纫普。這次就到這里了阅悍,不足之處,請多指教~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末昨稼,一起剝皮案震驚了整個濱河市节视,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悦昵,老刑警劉巖肴茄,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晌畅,死亡現(xiàn)場離奇詭異但指,居然都是意外死亡,警方通過查閱死者的電腦和手機抗楔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門棋凳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人连躏,你說我怎么就攤上這事剩岳。” “怎么了入热?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵拍棕,是天一觀的道長。 經常有香客問我勺良,道長绰播,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任尚困,我火速辦了婚禮蠢箩,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己谬泌,他們只是感情好滔韵,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掌实,像睡著了一般陪蜻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贱鼻,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天囱皿,我揣著相機與錄音,去河邊找鬼忱嘹。 笑死嘱腥,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拘悦。 我是一名探鬼主播齿兔,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼础米!你這毒婦竟也來了分苇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤屁桑,失蹤者是張志新(化名)和其女友劉穎医寿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蘑斧,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡靖秩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了竖瘾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沟突。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捕传,靈堂內的尸體忽然破棺而出惠拭,到底是詐尸還是另有隱情,我是刑警寧澤庸论,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布职辅,位于F島的核電站,受9級特大地震影響聂示,放射性物質發(fā)生泄漏域携。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一催什、第九天 我趴在偏房一處隱蔽的房頂上張望涵亏。 院中可真熱鬧宰睡,春花似錦、人聲如沸气筋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宠默。三九已至麸恍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搀矫,已是汗流浹背抹沪。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓤球,地道東北人融欧。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像卦羡,于是被迫代替她去往敵國和親噪馏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容

  • 轉載請注明出處:http://www.reibang.com/p/c65d9d753c31 在上一篇博客《數(shù)據結構...
    Alent閱讀 3,500評論 4 74
  • 大學的時候不好好學習,老師在講臺上講課拟赊,自己在以為老師看不到的座位看小說刺桃,現(xiàn)在用到了老師講的知識,只能自己看書查資...
    和玨貓閱讀 1,436評論 1 3
  • 好多天沒有更新文章吸祟,今天更新的是鏈表的復雜算法題瑟慈,這一篇完了之后,鏈表相關的也結束了欢搜,會進入下一個環(huán)節(jié)醋安。 圓圈中最...
    zero_sr閱讀 796評論 0 3
  • //leetcode中還有花樣鏈表題鹏氧,這里幾個例子,冰山一角 求單鏈表中結點的個數(shù)----時間復雜度O(n)這是最...
    暗黑破壞球嘿哈閱讀 1,514評論 0 6
  • 1.把二元查找樹轉變成排序的雙向鏈表 題目: 輸入一棵二元查找樹斩郎,將該二元查找樹轉換成一個排序的雙向鏈表第步。 要求不...
    曲終人散Li閱讀 3,299評論 0 19