0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

重新排列一個單鏈表

算法與數(shù)據(jù)結構 ? 來源:吳師兄學算法 ? 作者:吳師兄 ? 2022-10-10 09:39 ? 次閱讀

一、題目描述

給定一個單鏈表 L:L0→L1→…→Ln-1→Ln ,

將其重新排列后變?yōu)椋篖0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是單純的改變節(jié)點內部的值,而是需要實際的進行節(jié)點交換。

示例1:

給定鏈表 1->2->3->4->5, 重新排列為 1->5->2->4->3.

示例2:

給定鏈表 1->2->3->4, 重新排列為 1->4->2->3.

1a525330-47bb-11ed-a3b6-dac502259ad0.png

二、題目解析

這道題目很考察基本功和觀察能力,最終的結果就是將原鏈表的前半部分和原鏈表的后半部分反轉之后的鏈表進行合并得到的。

所以,需要執(zhí)行以下三個操作。

1、尋找出原鏈表的中點,把鏈表劃分為兩個區(qū)域

2、將右邊的鏈表進行反轉

3、把這兩個區(qū)域進行交錯合并

1、使用快慢指針尋找鏈表中點

在鏈表的頭節(jié)點設置兩個指針 slow、fast,同時將它們向后移動。

1a9fbd82-47bb-11ed-a3b6-dac502259ad0.png

每一次,slow 向后移動一步,fast 向后移動兩步。

1acce5aa-47bb-11ed-a3b6-dac502259ad0.png1b0d0af4-47bb-11ed-a3b6-dac502259ad0.png

于是,找到了中間節(jié)點 5,把鏈表劃分為兩個區(qū)域。

1b2a6f86-47bb-11ed-a3b6-dac502259ad0.png

2、將右邊的鏈表進行反轉

1b68b21e-47bb-11ed-a3b6-dac502259ad0.png

3、把這兩個區(qū)域進行交錯合并

屬于歸并排序的降維版本,這個操作不了解的話可以復習一下歸并排序

三、參考代碼

1、Java 代碼

//登錄AlgoMooc官網(wǎng)獲取更多算法圖解
//https://www.algomooc.com
//作者:程序員吳師兄
//代碼有看不懂的地方一定要私聊咨詢吳師兄呀
//重排鏈表(LeetCode 143):https://leetcode.cn/problems/reorder-list/
classSolution{
publicvoidreorderList(ListNodehead){
//a、尋找出原鏈表的中點,把鏈表劃分為兩個區(qū)域
//b、將右邊的鏈表進行反轉
//c、把這兩個區(qū)域進行交錯合并

//1、使用快慢指針尋找出鏈表的中點來
//*******************************************************
//對于1->2->3->4->5->6->7->8
//中間節(jié)點值為5
//所以左邊區(qū)域為1->2->3->4->5
//右邊區(qū)域為6->7->8
//但在視頻講解中,我把5歸為了右邊區(qū)域,這是一個錯誤
//雖然這個錯誤并不影響結果,因為合并過程都是一樣的邏輯
//*******************************************************
ListNodemid=middleNode(head);

//2、基于mid這個中點,將鏈表劃分為兩個區(qū)域

//左邊的區(qū)域開頭節(jié)點是head
ListNodeleftHead=head;

//右邊的區(qū)域開頭節(jié)點是mid.next
ListNoderightHead=mid.next;

//將鏈表斷開,就形成了兩個鏈表了
mid.next=null;

//3、將右邊的鏈表進行反轉
rightHead=reverseList(rightHead);

//4、將這兩個鏈表進行合并操作,即進行【交錯拼接】
while(leftHead!=null&&rightHead!=null){

//拼接過程如下
//5、先記錄左區(qū)域、右區(qū)域【接下來將有訪問的兩個節(jié)點】
ListNodeleftHeadNext=leftHead.next;

ListNoderightHeadNext=rightHead.next;

//6、左邊連接右邊的開頭
leftHead.next=rightHead;

//7、leftHead已經(jīng)處理好,移動到下一個節(jié)點,即剛剛記錄好的節(jié)點
leftHead=leftHeadNext;

//8、右邊連接左邊的開頭
rightHead.next=leftHead;

//9、rightHead已經(jīng)處理好,移動到下一個節(jié)點,即剛剛記錄好的節(jié)點
rightHead=rightHeadNext;

}

}

//LeetCode876:鏈表的中間節(jié)點
publicListNodemiddleNode(ListNodehead){

ListNodefast=head;

ListNodeslow=head;

while(fast!=null&&fast.next!=null){

fast=fast.next.next;

slow=slow.next;
}

returnslow;

}

//LeetCode206:反轉鏈表
publicListNodereverseList(ListNodehead){

//尋找遞歸終止條件
//1、head指向的結點為null
//2、head指向的結點的下一個結點為null
//在這兩種情況下,反轉之后的結果還是它自己本身
if(head==null||head.next==null)returnhead;

//不斷的通過遞歸調用,直到無法遞歸下去,遞歸的最小粒度是在最后一個節(jié)點
//因為到最后一個節(jié)點的時候,由于當前節(jié)點head的next節(jié)點是空,所以會直接返回head
ListNodecur=reverseList(head.next);

//比如原鏈表為1-->2-->3-->4-->5
//第一次執(zhí)行下面代碼的時候,head為4,那么head.next=5
//那么head.next.next就是5.next,意思就是去設置5的下一個節(jié)點
//等號右側為head,意思就是設置5的下一個節(jié)點是4

//這里出現(xiàn)了兩個next
//第一個next是「獲取」head的下一節(jié)點
//第二個next是「設置」當前節(jié)點的下一節(jié)點為等號右側的值
head.next.next=head;


//head原來的下一節(jié)點指向自己,所以head自己本身就不能再指向原來的下一節(jié)點了
//否則會發(fā)生無限循環(huán)
head.next=null;

//我們把每次反轉后的結果傳遞給上一層
returncur;
}
}

2、C++ 代碼

classSolution{
public:
voidreorderList(ListNode*head){
//a、尋找出原鏈表的中點,把鏈表劃分為兩個區(qū)域
//b、將右邊的鏈表進行反轉
//c、把這兩個區(qū)域進行交錯合并

//1、使用快慢指針尋找出鏈表的中點來
//*******************************************************
//對于1->2->3->4->5->6->7->8
//中間節(jié)點值為5
//所以左邊區(qū)域為1->2->3->4->5
//右邊區(qū)域為6->7->8
//但在視頻講解中,我把5歸為了右邊區(qū)域,這是一個錯誤
//雖然這個錯誤并不影響結果,因為合并過程都是一樣的邏輯
//*******************************************************
ListNode*mid=middleNode(head);

//2、基于mid這個中點,將鏈表劃分為兩個區(qū)域

//左邊的區(qū)域開頭節(jié)點是head
ListNode*leftHead=head;

//右邊的區(qū)域開頭節(jié)點是mid->next
ListNode*rightHead=mid->next;

//將鏈表斷開,就形成了兩個鏈表了
mid->next=nullptr;

//3、將右邊的鏈表進行反轉
rightHead=reverseList(rightHead);

//4、將這兩個鏈表進行合并操作,即進行【交錯拼接】
while(leftHead!=nullptr&&rightHead!=nullptr){

//拼接過程如下
//5、先記錄左區(qū)域、右區(qū)域【接下來將有訪問的兩個節(jié)點】
ListNode*leftHeadNext=leftHead->next;

ListNode*rightHeadNext=rightHead->next;

//6、左邊連接右邊的開頭
leftHead->next=rightHead;

//7、leftHead已經(jīng)處理好,移動到下一個節(jié)點,即剛剛記錄好的節(jié)點
leftHead=leftHeadNext;

//8、右邊連接左邊的開頭
rightHead->next=leftHead;

//9、rightHead已經(jīng)處理好,移動到下一個節(jié)點,即剛剛記錄好的節(jié)點
rightHead=rightHeadNext;

}
}



ListNode*middleNode(ListNode*head){
ListNode*slow=head;
ListNode*fast=head;
while(fast!=nullptr&&fast->next!=nullptr){
slow=slow->next;
fast=fast->next->next;
}
returnslow;
}


public:
ListNode*reverseList(ListNode*head){

//尋找遞歸終止條件
//1、head指向的結點為null
//2、head指向的結點的下一個結點為null
//在這兩種情況下,反轉之后的結果還是它自己本身
if(head==NULL||head->next==NULL)returnhead;

//不斷的通過遞歸調用,直到無法遞歸下去,遞歸的最小粒度是在最后一個節(jié)點
//因為到最后一個節(jié)點的時候,由于當前節(jié)點head的next節(jié)點是空,所以會直接返回head
ListNode*cur=reverseList(head->next);

//比如原鏈表為1-->2-->3-->4-->5
//第一次執(zhí)行下面代碼的時候,head為4,那么head.next=5
//那么head.next.next就是5.next,意思就是去設置5的下一個節(jié)點
//等號右側為head,意思就是設置5的下一個節(jié)點是4

//這里出現(xiàn)了兩個next
//第一個next是「獲取」head的下一節(jié)點
//第二個next是「設置」當前節(jié)點的下一節(jié)點為等號右側的值
head->next->next=head;


//head原來的下一節(jié)點指向自己,所以head自己本身就不能再指向原來的下一節(jié)點了
//否則會發(fā)生無限循環(huán)
head->next=nullptr;

//我們把每次反轉后的結果傳遞給上一層
returncur;

}

};

3、Python 代碼

classSolution:
defreorderList(self,head:ListNode)->None:
#a、尋找出原鏈表的中點,把鏈表劃分為兩個區(qū)域
#b、將右邊的鏈表進行反轉
#c、把這兩個區(qū)域進行交錯合并

#1、使用快慢指針尋找出鏈表的中點來
#*******************************************************
#對于1->2->3->4->5->6->7->8
#中間節(jié)點值為5
#所以左邊區(qū)域為1->2->3->4->5
#右邊區(qū)域為6->7->8
#但在視頻講解中,我把5歸為了右邊區(qū)域,這是一個錯誤
#雖然這個錯誤并不影響結果,因為合并過程都是一樣的邏輯
#*******************************************************
mid=self.middleNode(head)

#2、基于mid這個中點,將鏈表劃分為兩個區(qū)域

#左邊的區(qū)域開頭節(jié)點是head
leftHead=head

#右邊的區(qū)域開頭節(jié)點是mid.next
rightHead=mid.next

#將鏈表斷開,就形成了兩個鏈表了
mid.next=None

#3、將右邊的鏈表進行反轉
rightHead=self.reverseList(rightHead)

#4、將這兩個鏈表進行合并操作,即進行【交錯拼接】
whileleftHeadandrightHead:

#拼接過程如下
#5、先記錄左區(qū)域、右區(qū)域【接下來將有訪問的兩個節(jié)點】
leftHeadNext=leftHead.next

rightHeadNext=rightHead.next

#6、左邊連接右邊的開頭
leftHead.next=rightHead

#7、leftHead已經(jīng)處理好,移動到下一個節(jié)點,即剛剛記錄好的節(jié)點
leftHead=leftHeadNext

#8、右邊連接左邊的開頭
rightHead.next=leftHead

#9、rightHead已經(jīng)處理好,移動到下一個節(jié)點,即剛剛記錄好的節(jié)點
rightHead=rightHeadNext



defmiddleNode(self,head:ListNode)->ListNode:
slow=fast=head
whilefastandfast.next:
slow=slow.next
fast=fast.next.next
returnslow

defreverseList(self,head):
"""
:typehead:ListNode
ListNode
"""
#尋找遞歸終止條件
#1、head指向的結點為null
#2、head指向的結點的下一個結點為null
#在這兩種情況下,反轉之后的結果還是它自己本身
if(head==Noneorhead.next==None):
returnhead

#不斷的通過遞歸調用,直到無法遞歸下去,遞歸的最小粒度是在最后一個節(jié)點
#因為到最后一個節(jié)點的時候,由于當前節(jié)點head的next節(jié)點是空,所以會直接返回head
cur=self.reverseList(head.next)

#比如原鏈表為1-->2-->3-->4-->5
#第一次執(zhí)行下面代碼的時候,head為4,那么head.next=5
#那么head.next.next就是5.next,意思就是去設置5的下一個節(jié)點
#等號右側為head,意思就是設置5的下一個節(jié)點是4

#這里出現(xiàn)了兩個next
#第一個next是「獲取」head的下一節(jié)點
#第二個next是「設置」當前節(jié)點的下一節(jié)點為等號右側的值
head.next.next=head

#原來的下一節(jié)點指向自己,所以head自己本身就不能再指向原來的下一節(jié)點了
#否則會發(fā)生無限循環(huán)
head.next=None

#我們把每次反轉后的結果傳遞給上一層
returncur

四、復雜度分析

時間復雜度:O(N),其中 N 是鏈表中的節(jié)點數(shù)。

空間復雜度:O(1)。





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2943

    瀏覽量

    104110
  • python
    +關注

    關注

    53

    文章

    4753

    瀏覽量

    84081

原文標題:重排鏈表(LeetCode 143)

文章出處:【微信號:TheAlgorithm,微信公眾號:算法與數(shù)據(jù)結構】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    數(shù)據(jù)結構:鏈表的排序

    給定鏈表的頭結點head(該結點有值),長度為n的無序鏈表,對其按升序排序后,返回新
    的頭像 發(fā)表于 11-30 13:56 ?1143次閱讀
    數(shù)據(jù)結構:<b class='flag-5'>單</b><b class='flag-5'>鏈表</b>的排序

    約瑟夫環(huán)之循環(huán)鏈表這個程序題目大家知道做嗎

    題目:   n個人圍成圈(編號依次為:0,1,2...n-1),從第一個人開始報數(shù),1,2,……數(shù)到m者出列,再從下一個開始重新報數(shù),數(shù)到m者再出列……。 下面的程序中,用不帶附加表
    發(fā)表于 10-27 11:08

    鏈表的缺陷是什么

    鏈表定的缺陷,就是單向性,只能從結點到下一個節(jié)點,而不能訪問到上
    發(fā)表于 07-14 08:09

    RT-Thread內核中鏈表的使用與實現(xiàn)

    1. 鏈表與數(shù)組數(shù)組:線性數(shù)據(jù)結構,存放的數(shù)據(jù)的類型是樣的,而且他們在內存中的排布是有序排列的。因此數(shù)組的優(yōu)勢就是數(shù)據(jù)連續(xù),隨機訪問速度快,定義好了就不好在改變大小.
    發(fā)表于 04-01 12:01

    OpenHarmony中的HDF鏈表及其迭代器

    成員的值設置成第1節(jié)點的地址。因為鏈表只支持往方向查找,不支持往回查找,如上面的錯誤范例。如果root記錄的是第二
    發(fā)表于 08-30 10:31

    如何使用EB tresos自動重新排列和計算端口引腳ID?

    我指的是修改bootloader的附件文檔,只是參考3.4,你能告訴我如何使用EB tresos重新排列和自動計算端口pin ID嗎?而且如紅圈所指,我對新的端口ID很困惑,GPIO1_6_LED
    發(fā)表于 03-17 09:03

    C語言實現(xiàn)鏈表舉例

    所謂鏈表,就是用組任意的存儲單元存儲線性表元素的種數(shù)據(jù)結構。鏈表又分為鏈表、雙向
    發(fā)表于 07-11 16:40 ?87次下載
    C語言實現(xiàn)<b class='flag-5'>單</b><b class='flag-5'>鏈表</b>舉例

    鏈表——求兩城市的距離

    鏈表,鍵盤輸入城市名稱和城市的坐標,可以在菜單中選擇你要進行的內容
    發(fā)表于 11-26 15:45 ?1次下載

    合并兩排序的鏈表

    合并兩排序的鏈表、題目要求 輸入兩單調遞增的鏈表,輸出兩
    發(fā)表于 01-16 22:02 ?536次閱讀

    Linux USB總線的兩鏈表

    USB 總線引出兩首要 的鏈表,為 USB 設備
    發(fā)表于 04-20 10:33 ?920次閱讀

    鏈表學習的超詳細說明(二)

    昨天跟大家分享了鏈表些基本用法,今天接著繼續(xù)和大家分享鏈表的用法,今天分享完,
    的頭像 發(fā)表于 12-24 17:33 ?706次閱讀

    鏈表學習的總結(

    想必大多數(shù)人和我樣,剛開始學數(shù)據(jù)結構中的鏈表還是蠻吃力的,特別是后面的雙鏈表操作更是如此。還有就是在實踐代碼操作時,你又會感到無從下手,沒有思路。
    的頭像 發(fā)表于 12-24 17:35 ?3258次閱讀

    LeetCode876鏈表的中間結點介紹

    給定頭結點為 head 的非空鏈表,返回鏈表的中間結點。
    的頭像 發(fā)表于 01-11 17:58 ?754次閱讀
    LeetCode876<b class='flag-5'>鏈表</b>的中間結點介紹

    C語言的鏈表應用

    最近在看些開源項目,大佬的思路還是很值得去學習,今天就簡單介紹一下單鏈表的應用,配合回調函數(shù)可以玩出新花樣,廢話不多說直接看代碼!
    的頭像 發(fā)表于 02-20 15:03 ?515次閱讀

    鏈表和雙鏈表的區(qū)別在哪里

    鏈表和雙鏈表的區(qū)別 鏈表的每一個節(jié)點中只有指向下一個
    的頭像 發(fā)表于 07-27 11:20 ?1491次閱讀
    <b class='flag-5'>單</b><b class='flag-5'>鏈表</b>和雙<b class='flag-5'>鏈表</b>的區(qū)別在哪里