系統學習算法:專題十四 鏈表

前提知識:

1.畫圖,數據結構相關的題,畫圖必不可少,只要能畫出來,那么后面的代碼就很容易能寫出來,因為將抽象的數據結構轉換為直觀的圖畫

2.引入虛擬頭結點,也叫哨兵位,能夠避免考慮很多邊界情況

3.不要吝嗇空間,如果你看到第二點的時候,如果反應是有些題也可以不用虛擬節點就能解決時,其實這就已經是有點吝嗇空間的思想了,一個虛擬頭結點才幾個字節,根本不需要考慮,大膽創建就行了

4.快慢雙指針,鏈表中經常且好用的方法

5.鏈表常用存在,創建節點,頭插,尾插,其中頭插是比較重要的,因為通過頭插可以直接完成逆序鏈表的操作,而很多題目中都需要逆序鏈表,所以頭插的操作要掌握,同時又結合虛擬頭結點,那么頭插起來會非常方便

題目一:

算法原理:

題意很簡單,就是模擬加法的操作,只不過鏈表是逆序的,但是不要看是逆序的就覺得有難度,如果是正序的反而要更難一點,因為加法是從低位開始計算的,而逆序鏈表剛好就把低位放在了頭結點,反而更好操作,而正序鏈表如果要進行加法,反而要先逆序再操作

思路就是用兩個指針指向兩個鏈表的頭結點,然后開始往后遍歷,將兩數的和相加并記錄在一個變量t里面,最后該位只取t的個位,然后/=10即可,而循環遍歷結束條件是兩個指針都為空時,且t也為0才結束,其中為什么t也要為0是解決下面這種情況

?即雖然兩個指針為空,但還有一個進位沒有進

也是可以用虛擬頭結點,直接進行加法操作后,接在虛擬頭結點之后就行

代碼:

class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//虛擬頭結點ListNode newHead=new ListNode(0);//雙指針ListNode cur1=l1,cur2=l2,cur=newHead;//記錄進位int t=0;//循環遍歷while(cur1!=null||cur2!=null||t!=0){//如果鏈表1還有值if(cur1!=null){t+=cur1.val;cur1=cur1.next;}//如果鏈表2還有值if(cur2!=null){t+=cur2.val;cur2=cur2.next;}//進位操作cur.next=new ListNode(t%10);cur=cur.next;t/=10;}//返回真正的頭結點return newHead.next;}
}

題目二:

算法原理:

題意很簡單,注意不能修改原結點的值,只能通過移動結點進行修改

如果還是用之前剛學鏈表時的思想,那就是不創建虛擬結點,同時也吝嗇空間不定義變量,還不畫圖的話,那就腦袋里面慢慢繞了,一不小心就繞混了

因為如果不創建虛擬結點的話,12這兩個結點的操作和34之間的操作是不一樣的,34這里需要1這個結點指向4,而12這里沒有前面的結點,因此需要自己找頭結點,導致12的時候要特殊處理,不能進入循環,而創建虛擬頭結點就可以讓12操作和后面一樣

如果創建虛擬結點后并畫圖,但吝嗇空間的話,那么結點交換和修改就得考慮順序且非常亂

class Solution {public ListNode swapPairs(ListNode head) {if(head==null){return head;}ListNode newHead=new ListNode(0,head);ListNode prev=newHead,cur=head;while(cur!=null&&cur.next!=null){prev.next=cur.next;prev=cur;ListNode tmp=cur.next.next;cur.next.next=cur;cur.next=tmp;cur=tmp;}return newHead.next;}
}

?這循環里面的代碼順序不能亂調,且一眼看過去也很亂

而直接定義變量的話就會這樣

那么邏輯就直接是

prev指向next

next指向cur

cur指向nnext

且先后順序隨便,根本不影響

然后再修改變量,這里需要注意順序,不然會出現覆蓋

非常清晰

然后討論循環終止條件

偶數結點情況下:

可以發現,如果cur==null就終止

奇數結點情況下:

?可以發現,如果next==null就終止

代碼:

class Solution {public ListNode swapPairs(ListNode head) {//特殊處理空結點和單結點if(head==null||head.next==null){return head;}ListNode newHead=new ListNode(0,head);ListNode prev=newHead,cur=head,next=cur.next,nnext=next.next;while(cur!=null&&next!=null){//修改結點指向prev.next=next;next.next=cur;cur.next=nnext;//修改變量(注意順序)prev=cur;cur=nnext;if(cur!=null){next=cur.next;}if(next!=null){nnext=next.next;}}return newHead.next;}
}

題目三:

算法原理:

這道題比較綜合,通過模擬可以發現,無非是將前一半鏈表和后一半經過逆序的鏈表,進行合并即可,因此就涉及到三個知識點,找鏈表中間結點,逆序操作,合并鏈表

找鏈表中間結點,采用快慢雙指針來解決

逆序鏈表,采用頭插來解決

合并鏈表,模擬操作即可

需要注意的是,找到中間結點后,要將前后兩個鏈表之間進行切斷,實現兩個獨立的鏈表,才好方便進行后面的操作

代碼:

class Solution {public void reorderList(ListNode head) {if(head.next==null){return;}//找到中間結點ListNode slow=head;ListNode fast=head;while(fast!=null&&fast.next!=null){slow=slow.next;fast=fast.next.next;}//拆開鏈表ListNode cur=head;while(cur.next!=slow){cur=cur.next;}cur.next=null;cur=head;//逆序鏈表ListNode newHead=new ListNode();while(slow!=null){ListNode slowNext=slow.next;slow.next=newHead.next;newHead.next=slow;slow=slowNext;}//合并鏈表newHead=newHead.next;while(cur!=null&&newHead!=null){ListNode curNext=cur.next;ListNode newHeadNext=newHead.next;cur.next=newHead;if(curNext!=null){newHead.next=curNext;  }   cur=curNext;newHead=newHeadNext;}}
}

題目四:

?算法原理:

題意很簡單,就是按照升序合并k個鏈表,而且還比較好心的幫我們把k個鏈表進行了升序操作

我們之前學過合并2個鏈表,所以最容易想到的就是暴力解法,即遍歷數組,第1個鏈表和第2個鏈表合并之后,再和第3個鏈表合并……

時間復雜度上,假設有k個鏈表,每個鏈表長度為n

合并的時間復雜度是n(k-1)+n(k-2)……+n=O(nk^2)=O(N^3)

效率非常低,所以要換一個方法

方法一:

既然是比較大小進行排序,那么就可以借助優先級隊列來實現,將每個鏈表的頭結點都扔進去,取出堆頂元素,就是所有頭結點中最小的,然后對應的那個鏈表就扔下一個結點進去,然后再取堆頂元素,循環往復,直到對應鏈表為空,則停止添加,最后當堆為空時,則合并完成

時間復雜度上,堆的操作為log級別,有k個鏈表,所以要比較k次,又總共有n個節點,所以時間復雜度為O(n k logk)

代碼一(優先級隊列):

class Solution {public ListNode mergeKLists(ListNode[] lists) {//如果數組為空或者數組中的鏈表為空if(lists==null||lists.length==0){return null;}//創建一個小根堆PriorityQueue<ListNode> priorityQueue=new PriorityQueue<>((o1,o2)->o1.val-o2.val);//取出所有的頭結點放進去for(ListNode node:lists){if(node!=null){priorityQueue.offer(node);}}//創建一個虛擬節點ListNode newHead=new ListNode();ListNode prev=newHead;//合并鏈表while(!priorityQueue.isEmpty()){ListNode cur=priorityQueue.poll();prev.next=cur;prev=cur;//如果該鏈表為空則不添加if(cur.next!=null){priorityQueue.offer(cur.next);}}//返回頭結點return newHead.next;}
}

方法二:

既然合并k個鏈表可以拆分為n次合并2個鏈表,那么子問題是一樣的,就可以采用分治遞歸的思想,以歸并排序來解決,套模板即可

代碼二(分治遞歸):

class Solution {public ListNode mergeKLists(ListNode[] lists) {//要求對lists數組從下標0開始到lists.length-1之間的鏈表進行合并并返回頭結點return merge(lists,0,lists.length-1);}public ListNode merge(ListNode[] lists,int left,int right){//如果為空區間if(left>right){return null;}//如果只有一個鏈表,不用合并if(left==right){return lists[left];}//找到中間值,分為[left,mid],[mid+1,right]兩個區間int mid=(left+right)/2;//遞歸處理左右兩部分ListNode l1=merge(lists,left,mid);ListNode l2=merge(lists,mid+1,right);//合并兩個鏈表ListNode newHead=new ListNode();ListNode prev=newHead;while(l1!=null&&l2!=null){if(l1.val>=l2.val){prev.next=l2;prev=l2;l2=l2.next;}else{prev.next=l1;prev=l1;l1=l1.next;}}if(l1!=null){prev.next=l1;}if(l2!=null){prev.next=l2;}//返回頭結點return newHead.next;}
}

題目五:

算法原理:

題意很簡單,就是不停翻轉長度為k的鏈表,直到全部翻轉完或者剩余長度不夠k則停止

雖然是困難題,但是模擬的操作并不難

首先我們先遍歷一遍鏈表,統計出鏈表的長度,然后再除k看看有多少組需要被翻轉,假設為n組

然后循環n次,每次循環代表每一組,循環里面再嵌套循環k次,表示將k個結點進行翻轉

最后拼接上后面未被翻轉的結點

翻轉也就是逆序,所以我們采用之前用的頭插法

其中需要注意的是

每次頭插翻轉完一組后,后面結點跟的是第一次頭插的結點后面

代碼:

class Solution {public ListNode reverseKGroup(ListNode head, int k) {//如果翻轉長度為1,則不用翻轉if(k==1){return head;}//遍歷鏈表統計長度ListNode cur=head;int len=0;while(cur!=null){cur=cur.next;len++;}//如果長度不夠k,直接返回if(len<k){return head;}int n=len/k;//頭插翻轉cur=head;ListNode newHead=new ListNode(0);ListNode prev=newHead;ListNode tmp=null;//翻轉n組for(int i=0;i<n;i++){//記錄下一組的前驅結點tmp=cur; for(int j=0;j<k;j++){   ListNode next=cur.next;                  cur.next=prev.next;prev.next=cur;cur=next;}//更新前驅節點prev=tmp;}//拼接剩余節點prev.next=cur;//返回頭結點return newHead.next;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915769.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915769.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915769.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

零基礎學后端-PHP語言(第一期-PHP環境配置)

從本期開始&#xff0c;我們學習PHP&#xff0c;但是我們要先配置PHP環境 PHP官網鏈接&#xff1a;PHP For Windows: Binaries and sources Releases 我們可以看到有以下資源 可以看到有很多php的版本&#xff0c;有Non Thread Safe和Thread Safe&#xff0c;還有zip&#xf…

C++ primer知識點總結

《C Primer》系統學習指南&#xff1a;從C到C的平滑過渡根據你提供的《C Primer》目錄和你的需求&#xff08;C語言背景轉C&#xff0c;側重網絡編程&#xff09;&#xff0c;我將為你制定一個全面的學習計劃&#xff0c;包含知識點詳解、C/C對比、實戰案例和分階段項目練習。第…

異構融合 4A:重構高性能計算與復雜場景分析的安全與效率邊界

當全球數據量以每兩年翻一番的速度爆炸式增長&#xff0c;高性能計算&#xff08;HPC&#xff09;與復雜場景分析正成為破解氣候預測、基因測序、金融風控等世界級難題的關鍵引擎。但異構計算環境的碎片化、多系統協同的復雜性、數據流動的安全風險&#xff0c;正在形成制約行業…

【華為機試】240. 搜索二維矩陣 II

文章目錄240. 搜索二維矩陣 II描述示例 1示例 2提示解題思路核心分析問題轉化算法實現方法1&#xff1a;右上角開始搜索&#xff08;推薦&#xff09;方法2&#xff1a;逐行二分查找方法3&#xff1a;分治法方法4&#xff1a;左下角開始搜索復雜度分析核心要點數學證明右上角搜…

瘋狂星期四文案網第16天運營日記

網站運營第16天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 昨日訪問量 昨日30多ip, 今天也差不多&#xff0c;同步上周下降了一些&#xff0c;感覺明天瘋狂星期四要少很多了&#xff0c;記得上周四700多ip&…

Linux系統基礎入門與配置指南

Linux基本概述與配置 一、我們為什么使用Linux&#xff08;Linux的優點&#xff09;開源與自由 免費&#xff1a; 無需支付許可費用&#xff0c;任何人都可以自由下載、安裝和使用。源代碼開放&#xff1a; 任何人都可以查看、修改和分發源代碼。這帶來了極高的透明度、安全性和…

如何刪除VSCode Marketplace中的publisher

網頁上并沒有提供刪除的按鈕&#xff0c;需要通過命令的形式刪除。 vsce delete-publisher [要刪除的名字]# 鍵入token # y 確認這里的token是之前在Azure DevOps中創建的token&#xff0c;忘了的話可以重建一個 刷新網頁看一下 成功刪除了。

Windows安裝git教程(圖文版)

Git 是一個分布式版本控制系統&#xff0c;用于跟蹤文件的變化&#xff0c;特別是在軟件開發中。它使得多個開發者可以在不同的機器上并行工作&#xff0c;然后將他們的改動合并在一起。是在開發過程中&#xff0c;經常會用到的一個工具。本章教程&#xff0c;主要介紹Windows上…

Remote Framebuffer Protocol (RFB) 詳解

RFC 6143 規范文檔&#xff1a;The Remote Framebuffer Protocol 文章目錄1. 引言2. 初始連接流程2.1 TCP連接建立2.2 協議版本協商2.3 安全握手3. 顯示協議機制3.1 核心概念3.2 像素格式4. 輸入協議4.1 鍵盤事件(KeyEvent)4.2 鼠標事件(PointerEvent)5. 協議消息詳解5.1 握手消…

從 DeepSeek-V3 到 Kimi K2:八種現代大語言模型架構設計

編譯&#xff1a;青稞社區Kimi 原文&#xff1a;https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison 首發&#xff1a;https://mp.weixin.qq.com/s/lSM2jk1UxJVz1WllWYQ4aQ 自原始 GPT 架構開發以來已經過去了七年。乍一看&#xff0c;從 2019 年的…

linux驅動開發筆記--GPIO驅動開發

目錄 前言 一、設備樹配置 二、驅動編寫 三、用戶空間測試 總結 前言 開發平臺&#xff1a;全志A133&#xff0c;開發環境&#xff1a;linux4.9andrio10&#xff0c;開發板&#xff1a;HelperBoard A133_V2.5。 一、設備樹配置 打開板級設備樹配置文件&#xff0c;路徑&a…

騰訊iOA:企業軟件合規與安全的免費守護者

人們眼中的天才之所以卓越非凡&#xff0c;并非天資超人一等而是付出了持續不斷的努力。1萬小時的錘煉是任何人從平凡變成超凡的必要條件。———— 馬爾科姆格拉德威爾 目錄 一、為什么要使用騰訊iOA&#xff1f; 二、中小企業軟件合規痛點 三、騰訊iOA解決方案 3.1 核心技…

C#定時任務實戰指南:從基礎Timer到Hangfire高級應用

高效管理后臺作業&#xff0c;讓定時任務成為應用的可靠引擎 在C#應用開發中&#xff0c;定時任務是實現數據同步、報表生成、系統維護等后臺作業的核心技術。本文將深入探討C#生態中主流的定時任務解決方案&#xff0c;從基礎的內置Timer到強大的Quartz.NET和Hangfire框架&…

軟件開發、項目開發基本步驟

? 立項階段&#xff1a;項目定義、需求收集與分析、可行性分析、風險評估與規劃、項目團隊組建、制定項目計劃、獲取批準與支持。? 需求評審與分析&#xff1a;? 項目團隊&#xff08;包括產品經理、開發人員、測試人員等&#xff09;共同參與&#xff0c;明確項目的目標、功…

慢 SQL接口性能優化實戰

在對某電商項目進行接口性能壓測時&#xff0c;發現 /product/search 接口響應緩慢&#xff0c;存在明顯性能瓶頸。通過慢查詢日志排查和 SQL 優化&#xff0c;最終實現了接口響應速度的顯著提升。本文完整還原此次優化過程&#xff0c;特別強調操作步驟和問題分析過程&#xf…

【C#】在WinForms中實現控件跨TabPage共享的優雅方案

文章目錄一、問題背景二、基本實現方案1. 通過修改Parent屬性實現控件移動三、進階優化方案1. 創建控件共享管理類2. 使用用戶控件封裝共享內容四、方案對比與選擇建議五、最佳實踐建議六、完整示例代碼一、問題背景 在Windows窗體應用程序開發中&#xff0c;我們經常遇到需要…

Android Camera openCamera

由頭 今日調休&#xff0c;終于終于閑下來了&#xff0c;可以寫一下博客了&#xff0c;剛好打開自己電腦&#xff0c;就有四年前下的谷歌Android 12源碼&#xff0c;不是很舊&#xff0c;剛好夠用&#xff0c;不用再另外下載新源碼了&#xff0c;不得不感慨這時間過得真快啊~廢…

神經網絡——池化層

目錄 池化層 最大池化層 MaxPool2d 最大池化操作圖示 最大池化操作代碼演示 綜合代碼案例 池化層 池化層&#xff08;Pooling Layer&#xff09; 核心作用&#xff1a;通過降采樣減少特征圖尺寸&#xff0c;降低計算量&#xff0c;增強特征魯棒性。 1. 常見類型 …

Android 默認圖庫播放視頻沒有自動循環功能,如何添加2

Android 默認圖庫播放視頻沒有自動循環功能, 如何添加 按如下方式修改可以添加 開發云 - 一站式云服務平臺 --- a/packages/apps/Gallery2/src/com/android/gallery3d/app/MovieActivity.java +++ b/packages/apps/Gallery2/src/com/android/gallery3d/app/MovieActivity.java…

數字孿生賦能智慧能源電力傳輸管理新模式

在“雙碳”戰略和能源數字化轉型的雙重驅動下&#xff0c;智慧能源系統亟需更高效、精細和智能的管理手段。數字孿生技術作為融合物理世界與數字空間的橋梁&#xff0c;為電力傳輸系統的全生命周期管理提供了強有力的技術支撐。本文聚焦數字孿生在智慧能源電力傳輸中的應用&…