LeetCode刷題日記之K個一組翻轉鏈表

今天刷到LeetCode第25題蓬坡,記錄一下刷題的思路猿棉,方便以后回看。(真的一周不寫就容易忘啊屑咳,所以還是要多練)

這個題大概有三種解法:

  1. 借助棧先進后出的思路萨赁,當鏈表元素k個一組放進棧中,然后在拿出來兆龙。(缺點是時間復雜度較高杖爽,入棧出棧都要遍歷鏈表,不推薦紫皇,了解思路即可)慰安。
  2. 遞歸:k個一組進行遞歸,具體思路請參考后面圖解聪铺。
  3. 迭代:同上化焕,參考后面圖解。

話不多說计寇,先上代碼:

借助棧

        Deque<ListNode> stack = new LinkedList<>();

        ListNode dummy = new ListNode(0);
        //這個p會一直移動锣杂,需要另起一個指針
        ListNode p = dummy;
        while (true) {
            int count = 0;
            //這兒不能直接head移動,因為如果不足k個番宁,head指針將找不到元莫,因此另起一個指針
            ListNode temp = head;
            //k個一組放進棧中
            while (temp != null && count < k) {
                stack.push(temp);
                temp = temp.next;
                count++;
            }
            //當不足k個不用反轉
            if (count != k) {
                p.next = head;
                break;
            }
            while (!stack.isEmpty()) {
                p.next = stack.pop();
                p = p.next;
            }
            //將head替換為k個位置之后,方便下次循環(huán)
            head = temp;
        }

        return dummy.next;

這個思路其實沒啥好說的蝶押,用一個指針p依次的跟著棧往后移踱蠢,當不足k個時直接返回頭指針dummy.next。

看鏈表代碼的幾個重點思路

這里需要強調的幾點是在鏈表寫代碼時候的常識吧:

1、鏈表里面經(jīng)常會有如下代碼茎截,初學者看著會很奇怪苇侵,我一開始學習的時候也疑惑了很久:

        ListNode dummy = new ListNode(0);
        ListNode p = dummy;

這里已經(jīng)新創(chuàng)建了一個dummy,為啥還要再創(chuàng)建一個和dummy一樣的p企锌?為啥不直接用dummy?

原因是榆浓,當你新建的dummy節(jié)點在后續(xù)操作的時候,會進行移動撕攒,如p=p.next這種陡鹃,如果直接用dummy去操作,后面你拿到的dummy就不是原來的位置了抖坪,而是移動后的位置萍鲸,這時候如果你想要拿到原來的dummy,就需要新創(chuàng)建一個幫手去幫dummy做這個事擦俐,也就是p脊阴。

上面這個邏輯在鏈表代碼里面會經(jīng)常用到,搞懂其原因后在看代碼就不會有種云里霧里的感覺了蚯瞧。

2嘿期、鏈表里面經(jīng)常會出現(xiàn)下面這種xx.next滿天飛的情況,理解起來會很困難埋合。

head.next=next.next.next;
next.next=head;

你按照我的思路來想其實就會簡單很多秽五,當xx.next在表達式左邊的時候,就只有一個含義饥悴,xx的下一個節(jié)點指向表達式右邊的位置

head.next=next.next;就是指head指向next的下一個節(jié)點盲再。你這樣去梳理下以前看著比較懵逼的代碼就會清晰很多西设。反正我思路是清晰很多。

3答朋、一旦A.next=B之后贷揽,代表A以前指向的位置指針已經(jīng)斷開,現(xiàn)在指向B了梦碗。如果后續(xù)還需要用以前A.next的節(jié)點話就需要提前記錄下來禽绪。因此就會在鏈表了經(jīng)常出現(xiàn)下面這種代碼:

                ListNode next = tail.next.next;
                tail.next.next = prev.next;
                prev.next = tail.next;
                tail.next = next;

因為tail.next.next在第二行斷開了,第四行有需要用到洪规,所以會提前用Next把tail.next.next存起來印屁。

第三點乍看和第一點很像,但是還是有細微區(qū)別的斩例,一個是指針移動的記錄雄人,一個是指針斷開的記錄。

遞歸

        int count = 0;
        ListNode curr = head;
        while (curr != null && count < k) {
            curr = curr.next;
            count++;
        }

        if (count == k) {
            curr = reverseKGroup(curr, k);
            while (count-- > 0) {
                ListNode temp = head.next;
                head.next = curr;
                curr = head;
                head = temp;
            }
            head = curr;
        }

        return head;

代碼其實還是很清晰的念赶,我們一點一點來理一下础钠。

        int count = 0;
        ListNode curr = head;
        while (curr != null && count < k) {
            curr = curr.next;
            count++;
        }

上面這段代碼的主要目的就是k個節(jié)點分組恰力,這里ListNode curr=head;要用我上面的基本點想一下,由于如果直接用head后移的話初始的head位置就找不到了旗吁,因此需要一個幫手curr去幫它做這個事踩萎。

接著往下看,如果有k個節(jié)點很钓,就k個一組翻轉香府,否則直接返回。

curr = reverseKGroup(curr, k); 這個是遞歸調用履怯,這里需要說明的一點是在看遞歸相關代碼不要強行去人肉遞歸回还,你會很痛苦的,且會把自己搞暈叹洲,畢竟我們不是機器柠硕。我們只需要知道這個代碼后面得出的結果就是經(jīng)過reverseKGroup(curr, k)這個函數(shù)處理的curr就直接是k個一組翻轉好的結果,我們現(xiàn)在唯一要做的就是运提,將curr前面的k個元素翻轉好在組裝就行蝗柔。

而下面的代碼就是做的這個事:

            while (count-- > 0) {
                ListNode temp = head.next;
                head.next = curr;
                curr = head;
                head = temp;
            }
            head = curr;

這塊不是那么好理解,碰到這種情況一般畫個圖民泵,問題就迎刃而解了~

以head = [1,2,3,4,5], k = 3為例癣丧,圖畫的可能有點丑,但是可能會對你有點幫助栈妆。

遞歸示意圖

這里k=3胁编,因此只會循環(huán)三次,當最后循環(huán)完curr的位置是在頭結點鳞尔,head的位置在temp嬉橙,因此后面需要將head=curr。最后返回head即可寥假。

迭代

        int n = 0;
//        統(tǒng)計鏈表長度
        for (ListNode i = head; i != null; n++, i = i.next);

        ListNode dmy = new ListNode(0);
        dmy.next = head;
        for(ListNode prev = dmy, tail = head; n >= k; n -= k) {
            for (int i = 1; i < k; i++) {
                ListNode next = tail.next.next;
                tail.next.next = prev.next;
                prev.next = tail.next;
                tail.next = next;
            }

            prev = tail;
            tail = tail.next;
        }
        return dmy.next;

迭代的代碼可能會有點繞市框,xx.next.next這種樣式,不要有恐懼感糕韧,看懂之后思路還是很清晰的枫振。

前面兩行代碼是用n統(tǒng)計鏈表長度。其中遍歷鏈表的寫法還是很新穎的萤彩,值得借鑒粪滤。

下面在head前面構造了一個哨兵節(jié)點dummy,然后以dummy為prev,head為tail就開始翻轉了雀扶。

for(ListNode prev = dmy, tail = head; n >= k; n -= k)這塊代碼是將鏈表按k個一組開始進行翻轉额衙。

里面的for (int i = 1; i < k; i++)這里是從1開始而不是0開始,感覺會少循環(huán)一次。我是這么理解的窍侧,當有k個數(shù)县踢,其實真正翻轉的時候只會翻轉k-1次,這里1是沒問題的伟件。

具體的循環(huán)思路可以參考下圖硼啤,畫的有點亂,我相信你能看懂的~

迭代示意圖

一開始prev和tail是相鄰的斧账,tail.next.next=prev.next其實就是指將tail的下一個節(jié)點指向prev的下一個節(jié)點谴返,注意是tail的下一個節(jié)點不是tail。

然后prev.next=tail.next是指prev指向tail的下一個節(jié)點咧织。

tail.next=next是指tail指向原來tail.next.next位置嗓袱,這里不直接用tail.next.next是因為這個指針現(xiàn)在已經(jīng)斷掉了,所以用事先保存好的next习绢。

在k個一組翻轉完會將prev和tail進行重新賦值渠抹,在進行下一輪。

最后返回的是最開始的head節(jié)點位置闪萄,只是由于head現(xiàn)在已經(jīng)移動不知道到哪里去了梧却,所以用的是dummy.next,其實就是最開始的head位置败去。

寫在最后

這三個解法主要掌握遞歸和迭代即可放航,棧那個其實沒啥意義,時間復雜度太高圆裕,實際很難用到广鳍,除非什么特殊場景。

迭代和遞歸寫法其實都很繞吓妆,經(jīng)常會出現(xiàn)今天會寫搜锰,過一周看又有點不會了,所以多看多寫吧耿战,沒啥辦法,加油焊傅!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末剂陡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子狐胎,更是在濱河造成了極大的恐慌鸭栖,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件握巢,死亡現(xiàn)場離奇詭異晕鹊,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門溅话,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晓锻,“玉大人,你說我怎么就攤上這事飞几⊙舛撸” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵屑墨,是天一觀的道長躁锁。 經(jīng)常有香客問我,道長卵史,這世上最難降的妖魔是什么战转? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮以躯,結果婚禮上槐秧,老公的妹妹穿的比我還像新娘。我一直安慰自己寸潦,他們只是感情好色鸳,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著见转,像睡著了一般命雀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斩箫,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天吏砂,我揣著相機與錄音,去河邊找鬼乘客。 笑死狐血,一個胖子當著我的面吹牛,可吹牛的內容都是我干的易核。 我是一名探鬼主播匈织,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼牡直!你這毒婦竟也來了缀匕?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤碰逸,失蹤者是張志新(化名)和其女友劉穎乡小,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饵史,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡满钟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年胜榔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湃番。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡夭织,死狀恐怖,靈堂內的尸體忽然破棺而出牵辣,到底是詐尸還是另有隱情摔癣,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布纬向,位于F島的核電站择浊,受9級特大地震影響,放射性物質發(fā)生泄漏逾条。R本人自食惡果不足惜琢岩,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望师脂。 院中可真熱鬧担孔,春花似錦、人聲如沸吃警。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酌心。三九已至拌消,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間安券,已是汗流浹背墩崩。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侯勉,地道東北人鹦筹。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像址貌,于是被迫代替她去往敵國和親铐拐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容