LeetCode習(xí)題:排序鏈表

題目描述:給你鏈表的頭結(jié)點(diǎn) head 韭畸,請(qǐng)將其按 升序 排列并返回 排序后的鏈表 。

進(jìn)階:你可以在 O(n log n) 時(shí)間復(fù)雜度和常數(shù)級(jí)空間復(fù)雜度下备禀,對(duì)鏈表進(jìn)行排序嗎趁仙?

示例:

例1:
輸入:head = [4,2,1,3]
輸出:[1,2,3,4]

例2:
輸入:head = [-1,5,3,4,0]
輸出:[-1,0,3,4,5]

例3:
輸入:head = []
輸出:[]

提示:

  • 鏈表中節(jié)點(diǎn)的數(shù)目在范圍 [0, 5 * 104] 內(nèi)
  • -10^5 <= Node.val <= 10^5

以下解題思路均來(lái)自于LeetCode官方題解

題解一:自頂向下歸并排序

解題思路:找到鏈表的中點(diǎn),以中點(diǎn)為分界焚鲜,將鏈表拆分成兩個(gè)子鏈表掌唾。尋找鏈表的中點(diǎn)可以使用快慢指針的做法,快指針每次移動(dòng) 2 步忿磅,慢指針每次移動(dòng) 1 步糯彬,當(dāng)快指針到達(dá)鏈表末尾時(shí),慢指針指向的鏈表節(jié)點(diǎn)即為鏈表的中點(diǎn)葱她。對(duì)兩個(gè)子鏈表分別排序撩扒。將兩個(gè)排序后的子鏈表合并,得到完整的排序后的鏈表吨些〈曜唬可以使用「21. 合并兩個(gè)有序鏈表」的做法炒辉,將兩個(gè)有序的子鏈表進(jìn)行合并。上述過(guò)程可以通過(guò)遞歸實(shí)現(xiàn)挽拔。遞歸的終止條件是鏈表的節(jié)點(diǎn)個(gè)數(shù)小于或等于 1辆脸,即當(dāng)鏈表為空或者鏈表只包含 1 個(gè)節(jié)點(diǎn)時(shí),不需要對(duì)鏈表進(jìn)行拆分和排序螃诅。

解題語(yǔ)言:Swift

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class Solution {
    func sortList(_ head: ListNode?) -> ListNode? {
        if head == nil || head?.next == nil {
            return head
        }

        var fast = head, slow = head, temp = slow
        while (fast != nil) {
            temp = slow
            slow = slow?.next
            fast = fast!.next
            if fast != nil {
                fast = fast!.next
            }
        }
        temp?.next = nil
        let leftNode = sortList(head)
        let rightNode = sortList(slow)
        return merge(leftNode, rightNode)
    }

    func merge(_ first: ListNode?, _ second: ListNode?) -> ListNode? {
        let newNode = ListNode(-1)
        var temp = newNode
        var newFirst = first, newSecond = second
        while (newFirst != nil && newSecond != nil) {
            if (newFirst!.val <= newSecond!.val) {
                temp.next = newFirst
                newFirst = newFirst!.next
            } else {
                temp.next = newSecond
                newSecond = newSecond!.next
            }
            temp = temp.next!
        }

        if (newFirst != nil) {
            temp.next = newFirst
        }
1
        if (newSecond != nil) {
            temp.next = newSecond
        }
        return newNode.next
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(nlogn)啡氢,其中 n 是鏈表的長(zhǎng)度。
  • 空間復(fù)雜度:O(logn)术裸,其中 n 是鏈表的長(zhǎng)度倘是。空間復(fù)雜度主要取決于遞歸調(diào)用的椣眨空間搀崭。

題解二:自底向上歸并排序

解題思路:使用自底向上的方法實(shí)現(xiàn)歸并排序,則可以達(dá)到 O(1)的空間復(fù)雜度猾编。首先求得鏈表的長(zhǎng)度 length瘤睹,然后將鏈表拆分成子鏈表進(jìn)行合并。具體做法如下答倡。

  • 1.用 subLength 表示每次需要排序的子鏈表的長(zhǎng)度轰传,初始時(shí) subLength=1
  • 2.每次將鏈表拆分成若干個(gè)長(zhǎng)度為 subLength 的子鏈表(最后一個(gè)子鏈表的長(zhǎng)度可以小于 subLength),按照每?jī)蓚€(gè)子鏈表一組進(jìn)行合并瘪撇,合并后即可得到若干個(gè)長(zhǎng)度為 subLength×2的有序子鏈表(最后一個(gè)子鏈表的長(zhǎng)度可以小于 subLength×2)获茬。合并兩個(gè)子鏈表仍然使用「21. 合并兩個(gè)有序鏈表」的做法。
  • 3.將 subLength 的值加倍倔既,重復(fù)第 2 步恕曲,對(duì)更長(zhǎng)的有序子鏈表進(jìn)行合并操作,直到有序子鏈表的長(zhǎng)度大于或等于 length渤涌,整個(gè)鏈表排序完畢佩谣。

如何保證每次合并之后得到的子鏈表都是有序的呢?可以通過(guò)數(shù)學(xué)歸納法證明实蓬。

  • 1.初始時(shí) subLength=1茸俭,每個(gè)長(zhǎng)度為 1 的子鏈表都是有序的。
  • 2.如果每個(gè)長(zhǎng)度為 subLength 的子鏈表已經(jīng)有序瞳秽,合并兩個(gè)長(zhǎng)度為 subLength 的有序子鏈表瓣履,得到長(zhǎng)度為 subLength×2 的子鏈表,一定也是有序的练俐。
  • 3.當(dāng)最后一個(gè)子鏈表的長(zhǎng)度小于 subLength 時(shí)袖迎,該子鏈表也是有序的,合并兩個(gè)有序子鏈表之后得到的子鏈表一定也是有序的。

因此可以保證最后得到的鏈表是有序的燕锥。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class Solution {
    func sortList(_ head: ListNode?) -> ListNode? {
        if (head == nil) {
            return head
        }
        var length = 0
        var node = head
        while (node != nil) {
            length += 1
            node = node?.next
        }
        let dummyHead = ListNode(0, head)
        var subLength = 1
        while subLength < length {
            var prev = dummyHead, curr = dummyHead.next
            while curr != nil {
                let head1 = curr
                var i = 1
                while (i < subLength && curr != nil) {
                    curr = curr?.next
                    i += 1
                }
                let head2 = curr?.next
                curr?.next = nil
                curr = head2
                var j = 1
                while (j < subLength && curr != nil && curr?.next != nil) {
                    curr = curr?.next
                    j += 1
                }
                var next: ListNode?
                if curr != nil {
                    next = curr?.next
                    curr?.next = nil
                }
                let merged = merge(head1, head2)
                prev.next = merged
                while prev.next != nil {
                    prev = prev.next!
                }
                curr = next
            }
            subLength *= 2
        }
        return dummyHead.next
    }

    func merge(_ first: ListNode?, _ second: ListNode?) -> ListNode? {
        let newNode = ListNode(-1)
        var temp = newNode
        var newFirst = first, newSecond = second
        while (newFirst != nil && newSecond != nil) {
            if (newFirst!.val <= newSecond!.val) {
                temp.next = newFirst
                newFirst = newFirst!.next
            } else {
                temp.next = newSecond
                newSecond = newSecond!.next
            }
            temp = temp.next!
        }

        if (newFirst != nil) {
            temp.next = newFirst
        }
1
        if (newSecond != nil) {
            temp.next = newSecond
        }
        return newNode.next
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(nlogn)辜贵,其中 n 是鏈表的長(zhǎng)度。
  • 空間復(fù)雜度:O(1)归形。
題目來(lái)源:LeetCode
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末托慨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子暇榴,更是在濱河造成了極大的恐慌厚棵,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔼紧,死亡現(xiàn)場(chǎng)離奇詭異婆硬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)奸例,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)彬犯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人查吊,你說(shuō)我怎么就攤上這事谐区。” “怎么了逻卖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵宋列,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我箭阶,道長(zhǎng)虚茶,這世上最難降的妖魔是什么戈鲁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任仇参,我火速辦了婚禮,結(jié)果婚禮上婆殿,老公的妹妹穿的比我還像新娘诈乒。我一直安慰自己,他們只是感情好婆芦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布怕磨。 她就那樣靜靜地躺著,像睡著了一般消约。 火紅的嫁衣襯著肌膚如雪肠鲫。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天或粮,我揣著相機(jī)與錄音导饲,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛渣锦,可吹牛的內(nèi)容都是我干的硝岗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼袋毙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼型檀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起听盖,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胀溺,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后皆看,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體月幌,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年悬蔽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扯躺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝎困,死狀恐怖录语,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情禾乘,我是刑警寧澤澎埠,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站始藕,受9級(jí)特大地震影響蒲稳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伍派,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一江耀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诉植,春花似錦祥国、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至灼擂,卻和暖如春壁查,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剔应。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工睡腿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留康谆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓嫉到,卻偏偏與公主長(zhǎng)得像沃暗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子何恶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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