LeetCode Hot100 ---- 链表专题专题
鏈表
力扣109:將有序鏈表轉(zhuǎn)化為二叉搜素樹
力扣141:環(huán)形鏈表判斷是否有環(huán)
力扣142:環(huán)形鏈表檢測入口位置
力扣143:重拍鏈表
力扣160:相交鏈表
力扣206:反轉(zhuǎn)鏈表
力扣21:合并兩個有序鏈表
力扣23:合并K和有序鏈表
力扣234:回文聯(lián)表
力扣25:K個一組反轉(zhuǎn)鏈表
力扣328:奇偶鏈表
力扣445:鏈表求和(頭對齊:尾對齊)
力扣80:刪除排序數(shù)組中的重復(fù)元素
力扣82:刪除重復(fù)元素
力扣83:刪除排序鏈表中的重復(fù)元素
力扣86:分割鏈表
劍指offer:二叉搜索樹和雙向鏈表
劍指offer22:鏈表中倒數(shù)第K個節(jié)點(diǎn)
劍指offer54:二叉搜索樹中的第K大節(jié)點(diǎn)
LRU實(shí)現(xiàn)
從尾到頭打印鏈表
鏈表相關(guān)的核心點(diǎn)
- null/nil 異常處理
- dummy node 啞巴節(jié)點(diǎn)
- 快慢指針
- 插入一個節(jié)點(diǎn)到排序鏈表
- 從一個鏈表中移除一個節(jié)點(diǎn)
- 翻轉(zhuǎn)鏈表
- 合并兩個鏈表
- 找到鏈表的中間節(jié)點(diǎn)
206. 反轉(zhuǎn)鏈表
反轉(zhuǎn)一個單鏈表。
https://zengwenqi.blog.csdn.net/article/details/116308095
思路:
迭代解法,兩個指針,一前一后遍歷過去。后一個到鏈表結(jié)尾時,前一個就是反轉(zhuǎn)后鏈表的頭結(jié)點(diǎn)。注意所謂雙指針,并不是指向某個節(jié)點(diǎn),而是指針就是節(jié)點(diǎn)。
class Solution:def reverseList(self, head: ListNode) -> ListNode:if head is None:return headleft, right = None, head # 用right是便于理解,也可以直接用headwhile right:tmp = right.nextright.next = leftleft = rightright = tmpreturn left # -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution:# 返回ListNodedef ReverseList(self, pHead):# write code heredummy = ListNode(0)while pHead:r = pHead.nextpHead.next = dummy.nextdummy.next = pHeadpHead = rreturn dummy.next遞歸解法:
class Solution:def reverseList(self, head: ListNode) -> ListNode:if not head:return headdef helper(head):if not head.next:return headnewhead = self.reverseList(head.next)# 以鏈表1234為例,調(diào)用完遞歸后,整個鏈表形如1 -> 2 <- 3 <- 4,newhead為4head.next.next = head # 即2.next = 1head.next = None # 即1.next=Nonereturn newheadreturn helper(head)reverse-linked-list-ii
反轉(zhuǎn)從位置 ?m? 到 ?n? 的鏈表。請使用一趟掃描完成反轉(zhuǎn)。
- 思路:先找到 m 處, 再反轉(zhuǎn) n - m 次即可
234. 回文鏈表
判斷一個鏈表是否為回文鏈表。
思路:
按順序保存鏈表中的值到數(shù)組中,查看是否為回文數(shù)組。
class Solution:def isPalindrome(self, head: ListNode) -> bool:l = []while head:l.append(head.val)head = head.nextreturn l==l[::-1]O(1) 空間復(fù)雜度的解法需要破壞原鏈表(找中點(diǎn) -> 反轉(zhuǎn)后半個list -> 判斷回文),在實(shí)際應(yīng)用中往往還需要復(fù)原(后半個list再反轉(zhuǎn)一次后拼接),操作比較復(fù)雜,這里給出更工程化的做法
class Solution:def isPalindrome(self, head: ListNode) -> bool:s = []slow = fast = headwhile fast is not None and fast.next is not None:s.append(slow.val)slow = slow.nextfast = fast.next.nextif fast is not None:slow = slow.nextwhile len(s) > 0:if slow.val != s.pop():return Falseslow = slow.nextreturn True141. 環(huán)形鏈表
判斷鏈表中是否有環(huán)。
class Solution:def hasCycle(self, head: ListNode) -> bool:if not head or not head.next:return Falseslow = headfast = head.nextwhile fast and fast.next:if slow == fast:return Trueslow = slow.nextfast = fast.next.nextreturn False21. 合并兩個有序鏈表
將兩個升序鏈表合并為一個新的 升序 鏈表并返回。新鏈表是通過拼接給定的兩個鏈表的所有節(jié)點(diǎn)組成的。
輸入:1->2->4, 1->3->4輸出:1->1->2->3->4->4思路:
迭代
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:if not l1: return l2if not l2: return l1?if l2.val < l1.val:l1, l2 = l2,l1dummy = cur = ListNode()dummy = l1while l1 and l2:if l2.val < l1.val:l1, l2 = l2,l1cur.next = l1cur = cur.nextl1 = l1.nextif not l1: cur.next = l2elif not l2: cur.next = l1return dummy遞歸
class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:if not l1: return l2 # 終止條件,一個為空返回另一個if not l2: return l1if l1.val < l2.val: l1.next = self.mergeTwoLists(l1.next,l2)return l1else:l2.next = self.mergeTwoLists(l1,l2.next)return l2160. 相交鏈表
編寫一個程序,找到兩個單鏈表相交的起始節(jié)點(diǎn)。沒有則返回None.
思路: 如果有相交點(diǎn), 兩個鏈表從相交的那個點(diǎn)開始到結(jié)尾的長度是一樣的.
class Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:def get_len(head): # 計(jì)算鏈表長度(節(jié)點(diǎn)數(shù))length = 0while head:length+=1head = head.nextreturn lengthlenA = get_len(headA)lenB = get_len(headB)if lenA>lenB: # 統(tǒng)一為A長度小于等于BheadA, headB = headB, headAlenA, lenB = lenB, lenAfor i in range(lenB - lenA):headB = headB.next # 長的鏈表先走幾步, 使兩鏈表等長,然后開始遍歷while headA:if headA==headB:return headAheadA = headA.nextheadB = headB.nextreturn142. 環(huán)形鏈表 II
給定一個鏈表,返回鏈表開始入環(huán)的第一個節(jié)點(diǎn)。 如果鏈表無環(huán),則返回 null。
思路:
快慢指針,一個每次走一步,另一個每次走兩步,走x步后相遇,則兩指針分別走了x和2x步,且其相遇時必然在環(huán)內(nèi)某處(如果有環(huán)),且快指針比慢指針多走了環(huán)的長度,即環(huán)的長度為x。
用L表示入環(huán)之前的長度,gap表示入環(huán)到相遇點(diǎn)的長度,則有L+gap=x,即L=x-gap,如果從相遇點(diǎn)繼續(xù)走x-gap長度,正好會走到入環(huán)處。因此兩指針一個從其實(shí)處走L,另一個從相遇點(diǎn)走x-gap,正好會在入環(huán)點(diǎn)相遇,返回即可。
class Solution:def detectCycle(self, head: ListNode) -> ListNode:slow, fast = head, headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextif slow == fast:fast = headcnt = 0 while fast != slow:slow = slow.nextfast = fast.nextcnt +=1return slowreturn19. 刪除鏈表的倒數(shù)第N個節(jié)點(diǎn)
給定一個鏈表,刪除鏈表的倒數(shù)第 n 個節(jié)點(diǎn),并且返回鏈表的頭結(jié)點(diǎn)。
思路:
兩個指針一前一后,一次遍歷。
注意特例是head有可能被刪除,因此不能直接返回head,而引入dummy指向head,如果head被刪除了,dummy也會指向head的下一個。
class Solution:def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:dummy = ListNode()dummy.next = headfast = slow = dummyfor i in range(n):fast = fast.nextwhile fast.next:fast, slow = fast.next, slow.nextslow.next = slow.next.nextreturn dummy.next2. 兩數(shù)相加
給你兩個 非空 的鏈表,表示兩個非負(fù)的整數(shù)。它們每位數(shù)字都是按照 逆序的方式存儲的,并且每個節(jié)點(diǎn)只能存儲一位數(shù)字。
請你將兩個數(shù)相加,并以相同形式返回一個表示和的鏈表。
思路:
梳理清楚所有的可能性。
# Definition for singly-linked list. # class ListNode(object): # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution(object):def addTwoNumbers(self, l1, l2):""":type l1: ListNode:type l2: ListNode:rtype: ListNode"""carry = 0res = ListNode(0)head = reswhile l1 or l2 or carry != 0:sum = carryif l1:sum += l1.vall1 = l1.nextif l2:sum += l2.vall2 = l2.nextif sum < 10:res.val = sumcarry = 0else:res.val = sum % 10carry = 1if l1 or l2 or carry != 0:res.next = ListNode(0)res = res.nextreturn head23. 合并K個升序鏈表
給你一個鏈表數(shù)組,每個鏈表都已經(jīng)按升序排列。
請你將所有鏈表合并到一個升序鏈表中,返回合并后的鏈表。
思路:
分治法,K個鏈表,N個節(jié)點(diǎn),則時間復(fù)雜度為NlogK。
因?yàn)榉种问莾蓛珊喜?#xff0c;如16個鏈表,第一次合并為8個,第二次合并為4個,第三次2個,第四次合并結(jié)束。因此合并logK輪。每輪合并時,每次比較都能得到抽出一個節(jié)點(diǎn),因此比較的次數(shù)就等于節(jié)點(diǎn)數(shù),每輪均為N。
如果是一次合并,即第一次1和2合并,之后12和3合并,之后123和4合并的話,復(fù)雜度則為N^2.
class Solution:def mergeKLists(self, lists: List[ListNode]) -> ListNode:if not lists:returnself.lists = listsreturn self.splitMerge(0,len(lists)-1)def splitMerge(self,left,right):# 不斷二分,直到每次不能再分if left==right:return self.lists[left]mid = left + (right-left)//2left_ll = self.splitMerge(left,mid)right_ll = self.splitMerge(mid+1,right)return self.merge2Lists(left_ll, right_ll)def merge2Lists(self, left_ll, right_ll):if not left_ll: return right_llif not right_ll: return left_llif left_ll.val > right_ll.val:left_ll, right_ll = right_ll, left_llleft_ll.next = self.merge2Lists(left_ll.next,right_ll)return left_llreorder-list
方法一:線性表
因?yàn)殒湵聿恢С窒聵?biāo)訪問,所以我們無法隨機(jī)訪問鏈表中任意位置的元素。
因此比較容易想到的一個方法是,我們利用線性表存儲該鏈表,然后利用線性表可以下標(biāo)訪問的特點(diǎn),直接按順序訪問指定元素,重建該鏈表即可。
方法二:尋找鏈表中點(diǎn) + 鏈表逆序 + 合并鏈表
注意到目標(biāo)鏈表即為將原鏈表的左半端和反轉(zhuǎn)后的右半端合并后的結(jié)果。
這樣我們的任務(wù)即可劃分為三步:
??? 找到原鏈表的中點(diǎn)(參考「876. 鏈表的中間結(jié)點(diǎn)」)。
??????? 我們可以使用快慢指針來 O(N)地找到鏈表的中間節(jié)點(diǎn)。
??? 將原鏈表的右半端反轉(zhuǎn)(參考「206. 反轉(zhuǎn)鏈表」)。
??????? 我們可以使用迭代法實(shí)現(xiàn)鏈表的反轉(zhuǎn)。
??? 將原鏈表的兩端合并。
??????? 因?yàn)閮涉湵黹L度相差不超過 1,因此直接合并即可
sort-list
在 ?O(n?log?n) 時間復(fù)雜度和常數(shù)級空間復(fù)雜度下,對鏈表進(jìn)行排序。
- 思路:歸并排序,slow-fast找中點(diǎn)
注意點(diǎn)
- 快慢指針 判斷 fast 及 fast.next 是否為 nil 值
- 遞歸 mergeSort 需要斷開中間節(jié)點(diǎn)
- 遞歸返回條件為 head 為 nil 或者 head.Next 為 nil
練習(xí)
- remove-duplicates-from-sorted-list
- remove-duplicates-from-sorted-list-ii
- reverse-linked-list
- reverse-linked-list-ii
- merge-two-sorted-lists
- partition-list
- sort-list
- reorder-list
- linked-list-cycle
- linked-list-cycle-ii
- palindrome-linked-list
- copy-list-with-random-pointer
總結(jié)
以上是生活随笔為你收集整理的LeetCode Hot100 ---- 链表专题专题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年花呗上不上征信,当然上
- 下一篇: 阿里云盘今晚出现故障,无法访问和加载内容