Leetcode 單鏈表題型總結
自己刷題的一個小記錄,難度從easy --> hard,持續更新中。若有更好的方法,歡迎評論區討論呀。
876. 返回單鏈表的中間節點。如果有兩個中間節點,返回第二個。
兩種思路,
第一種是先遍歷一遍,記錄長度後,再遍歷到一半。時間複雜度O(n)
第二種是用兩個指針,一快一慢,快指針每次走兩步,慢指針每次走一步,當快指針走到鏈表最後時,慢指針所指的就是中間節點。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
'''
先遍歷一遍,記錄長度,再遍歷一半。
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
if head.next ==None:
return head
length = 1
phead = head
while head.next !=None:
length +=1
head = head.next
mid = length//2
for i in range(mid):
phead = phead.next
return phead
'''
# 直接快慢指針,快的到頭以後,慢的就是中間值
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
if head.next ==None:
return head
fast = head
slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
206. 反轉單鏈表
兩種思路
遞歸
迭代
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
'''
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# 遞歸版本,一直往後遍歷,每次建立一個反轉鏈接
return self.reverse(head,None)
def reverse(self, current, prev):
if current == None:
return prev
nxt = current.next
current.next = prev
prev = current
current = nxt
return self.reverse(current, prev)
'''
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# 迭代版本
return self.reverse(head,None)
def reverse(self, current, prev):
if current == None:
return prev
while current!=None:
nxt = current.next
current.next = prev
prev = current
current = nxt
return prev
237. 刪除單鏈表中的某一節點
這題要注意的是,並沒有給出完整的鏈表,函數參數中只是給了要刪除的節點。
但由於是單鏈表,沒法獲取被刪除節點前的節點,所以將被刪除節點的值改爲後面一個節點的值,然後刪除後面一個節點即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
# 沒有給完整的單鏈表,只是給了要刪除的節點!沒有head!
node.val = node.next.val
node.next = node.next.next
21. 合併兩個已排序的單鏈表
用兩個指針分別遍歷兩個鏈表。遇到較小的一個,就加到合併列表後面,並指針後移。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
newlist = ListNode(0)
tmp = newlist
while l1!=None and l2!=None:
if l1.val<l2.val:
tmp.next = l1
l1 = l1.next
else:
tmp.next = l2
l2 = l2.next
tmp = tmp.next
if l1!=None:
while l1!=None:
tmp.next = l1
l1 = l1.next
tmp = tmp.next
if l2!=None:
while l2!=None:
tmp.next = l2
l2 = l2.next
tmp = tmp.next
return newlist.next
83.給定一個排序後的單鏈表,刪除其中的重複節點,保證每個節點只出現一次。
思路:遇到重複的節點,就往後遍歷,直到節點不重複即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
tmp = head
while tmp!=None:
nxt = tmp.next
while nxt!=None and nxt.val== tmp.val:
nxt = nxt.next
tmp.next = nxt
tmp = nxt
return head
141. 判斷單鏈表是否有環
思路:用快慢指針,如果有環,那麼兩個指針一定會相遇。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
# 快慢指針,如果有環,那麼總歸會相遇
if head==None or head.next==None:
return False
fast = head.next
slow = head
flag = 0
while fast!=slow:
if fast==None or fast.next==None:
return False
fast = fast.next.next
slow = slow.next
return True
234.判斷一個單鏈表是否是迴文的。
思路1: 笨方法,遍歷鏈表,將鏈表的節點值存儲在數組中,再分別比較數組的頭尾。
思路2: 先遍歷一遍列表,記錄長度。然後將鏈表分成兩半,前面一半進行反轉,然後與後面一半比較是否完全相等。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
'''另外用一個數組將鏈表節點的值存下來,空間複雜度O(N)'''
'''
def isPalindrome(self, head):
vals = []
while head:
vals += head.val,
head = head.next
return vals == vals[::-1]
'''
def isPalindrome(self, head: ListNode) -> bool:
# 先遍歷一遍鏈表,記錄鏈表長度。
# 然後從中間將鏈表切成前後兩半,並把前面一半進行反轉,然後與後面一半進行比較
if head==None or head.next==None:
return True
node = head
count = 0
while node!=None:
count+=1
node=node.next
node = head
prev = None
for i in range(count//2):
nxt = node.next
node.next = prev
prev = node
node = nxt
if count%2==0:
h2 = node
else:
h2 = node.next
h1 = prev
while h1 and h2:
if h1.val!=h2.val:
return False
h1 = h1.next
h2 = h2.next
return True
203. 給定一個鏈表和一個值val,將鏈表中值爲val的節點都刪除。
注意考慮頭部節點需要刪除的情況即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
if head==None:
return head
if head.next==None:
if head.val == val:
return None
else:
return head
while head!=None and head.val==val:
head=head.next
node = head
while node!=None:
if node.next!=None and node.next.val ==val:
node.next = node.next.next
else:
node = node.next
return head
160. 找兩個鏈表的交點。
思路1: 先分別遍歷一遍兩個鏈表,記錄他們的長度l1和l2,讓長的那個鏈表先走|l1-l2|步,再同時走,就能走到交點處。
思路2: 更簡潔的一個方法,兩個鏈表同時從頭節點走,其中一個走到頭後,redirect到另一個鏈表的頭節點。這樣就能找到交點。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
'''
先分別記錄兩個鏈表的長度,然後讓長的那個先走“差值”步,再同時走,就能一起走到交點處
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
if headA == None or headB==None:
return None
node1 = headA
node2 = headB
length1, length2 = 0, 0
while node1!=None:
length1+=1
node1 = node1.next
while node2!=None:
length2+=1
node2 = node2.next
if length1>length2:
for i in range(length1-length2):
headA = headA.next
else:
for i in range(length2-length1):
headB = headB.next
while headA!=headB:
headA = headA.next
headB = headB.next
return headA
'''
'''
更簡潔的一個方法,兩個鏈表同時從頭節點走,其中一個走到頭後,redirect到另一個鏈表的頭節點。這樣就能找到交點'''
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
if headA == None or headB==None:
return None
node1 = headA
node2 = headB
while node1!=node2:
node1 = headB if node1==None else node1.next
node2 = headA if node2==None else node2.next
return node1
707. 實現一個單鏈表類,包括一系列添加節點、刪除節點的函數。
class listNode:
def __init__(self, val):
self.val = val
self.next = None
class MyLinkedList(object):
def __init__(self):
"""
Initialize your data structure here.
"""
self.head = None
self.size = 0
def get(self, index):
"""
Get the value of the index-th node in the linked list. If the index is invalid, return -1.
:type index: int
:rtype: int
"""
if index<0 or index>=self.size:
return -1
if self.head is None:
return -1
node = self.head
for i in range(index):
node = node.next
return node.val
def addAtHead(self, val):
"""
Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
:type val: int
:rtype: None
"""
newHead = listNode(val)
newHead.next = self.head
self.head = newHead
self.size +=1
def addAtTail(self, val):
"""
Append a node of value val to the last element of the linked list.
:type val: int
:rtype: None
"""
newnode = listNode(val)
node = self.head
if node ==None:
self.head = newnode
else:
while node.next!=None:
node = node.next
node.next = newnode
self.size+=1
def addAtIndex(self, index, val):
"""
Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
:type index: int
:type val: int
:rtype: None
"""
if index>0 and index <= self.size:
node = self.head
for i in range(index-1):
node = node.next
newnode = listNode(val)
newnode.next = node.next
node.next = newnode
self.size +=1
elif index<=0:
self.addAtHead(val)
def deleteAtIndex(self, index):
"""
Delete the index-th node in the linked list, if the index is valid.
:type index: int
:rtype: None
"""
if index>0 and index<self.size:
node = self.head
for i in range(index-1):
node = node.next
node.next = node.next.next
self.size -=1
elif index==0:
self.head = self.head.next
self.size -=1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
1019.給定一個單鏈表,返回一個數組,數組中第i個元素是鏈表中第i個節點的next greater值,即最近的比第i個節點值大的節點值。
example:
Input: [1,7,5,1,9,2,5,1]
Output: [7,9,9,9,0,5,0,0]
思路:用一個棧來存儲‘還沒有遇到next greater的節點’,每次當前節點與棧頂進行比較。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
'''
用一個stack來存儲‘還沒有遇到next greater的節點’,每次當前節點與棧頂進行比較 '''
class Solution:
def nextLargerNodes(self, head: ListNode) -> List[int]:
if head==None:
return None
elif head.next == None:
return [0]
ans = []
stack = []
pos = -1
while head:
pos +=1
ans.append(0)
# 把棧頂元素和當前節點進行比較
while stack and stack[-1][1]<head.val:
# 小的話,就把棧頂元素彈出,並且寫到ans數組中
# 直到棧爲空
index, _ = stack.pop()
ans[index] = head.val
stack.append((pos,head.val))
head = head.next
return ans
817. 單鏈表的組成部分
給定一個單鏈表和一個數組,數組是單鏈表節點值的子集。返回一個number,代表數組中的值在單鏈表中分爲幾個相連部分。
example:
輸入:
head: 0->1->2->3
G = [0, 1, 3]
輸出: 2
Explanation: 0 and 1 are connected, so [0, 1] and [3] are the two connected components.
思路:遍歷單鏈表的節點,如果節點值在數組中出現,那麼往後遍歷,直到不出現則出現斷點,就表示number+=1。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
'''
def numComponents(self, head: ListNode, G: List[int]) -> int:
# 遍歷單鏈表,然後看連續的鏈表部分是否出現在list中
node = head
count = 0
while node!=None:
if node.val in G:
while node!=None and node.val in G:
node = node.next
count+=1
if node!=None:
node = node.next
return count
'''
#更簡潔的寫法
def numComponents(self, head, G):
setG = set(G)
res = 0
while head:
if head.val in setG and (head.next == None or head.next.val not in setG):
res += 1
head = head.next
return res
445. 給定兩個單鏈表,分別表示兩個整數,返回兩個整數的和。且題目要求不能反轉鏈表。
example:
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
思路:由於不能反轉鏈表,那麼我想到的只能是把數存下來,相加後再構造新的鏈表。有更好的方法會再更新。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
# 不能反轉鏈表
# 第一個方法,將鏈表遍歷後,得到數字,相加後再構造一個鏈表
num1, num2 = 0,0
while l1!=None:
num1 = num1*10 + l1.val
l1 = l1.next
while l2!=None:
num2 = num2*10 + l2.val
l2 = l2.next
x = num1+num2
head = ListNode(0)
if x == 0: return head
while x:
v, x = x%10, x//10
head.next, head.next.next = ListNode(v), head.next
return head.next
328. 給定一個單鏈表,把鏈表中的奇數節點都放到前面,偶數節點放到後面,且不改變奇數之間和偶數之間的相對順序。
思路:與86題相似,建立兩個空節點,然後奇數偶數分別連到後面,再合併鏈表。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def oddEvenList(self, head):
dummy1 = odd = ListNode(0)
dummy2 = even = ListNode(0)
while head:
odd.next = head
even.next = head.next
odd = odd.next
even = even.next
head = head.next.next if even else None
odd.next = dummy2.next
return dummy1.next
725. 給定一個單鏈表,和一個數字k,要求把鏈表分爲k個部分,且每個部分的元素個數相差不超過1。
example:
Input: root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
思路:首先考慮k比鏈表長度小的情況,直接分割即可。其他的,先遍歷鏈表,記錄長度len,len//k表示每個組元素的最少個數,len%k=num表示多的元素個數,分別在前num個塊中加上1個元素。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
# 先記錄鏈表長度length,然後length/k,餘數yushu表示多的,那麼在前yushu個段加1個
node = root
length = 0
while node!=None:
length+=1
node = node.next
ans =[]
node = root
shang = length//k
yushu = length%k
for i in range(k):
head = ListNode(0)
s = head
if i<yushu:
for j in range(shang+1):
head.next = node
node = node.next
head = head.next
else:
for j in range(shang):
head.next = node
node = node.next
head = head.next
head.next = None
ans.append(s.next)
return ans
24. 交換鏈表中每兩個相鄰的節點。
example:
Given 1->2->3->4, you should return the list as 2->1->4->3.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def swapPairs(self, head):
pre, pre.next = self, head
while pre.next and pre.next.next:
a = pre.next
b = a.next
pre.next, b.next, a.next = b, a, b.next
pre = a
return self.next
430. 把一個層級的,含有子節點的雙向鏈表進行flatten,表示成一個新的雙向鏈表。
example:
思路:用一個棧來存儲4、9節點。即遇到有子節點的節點,將後面的節點入棧。遍歷到最後一層的最後節點後,開始出棧,將節點加到後面,直到棧爲空。
"""
# Definition for a Node.
class Node:
def __init__(self, val, prev, next, child):
self.val = val
self.prev = prev
self.next = next
self.child = child
"""
class Solution:
# 每次遇到有child節點的節點,就把next入棧。遍歷到最後之後,再從棧裏pop出節點加在後面
def flatten(self, head: 'Node') -> 'Node':
stack = []
node = head
prev = node
while node:
if node.child!=None:
if node.next:
stack.append(node.next)
node.child.prev = node
node.next = node.child
node.child = None
if not node.next and len(stack):
curr = stack.pop()
curr.prev = node
node.next = curr
node = node.next
return head
1171. 給定一個單鏈表,刪除其中和爲0的部分。
example:
Input: head = [1,2,-3,3,1]
Output: [3,1]
Note: The answer [1,2,1] would also be accepted.
思路:用一個字典來存儲當前的累加值,如果累加值出現重複,那麼說明有和爲0的部分存在。把這部分節點刪去,同時注意刪去字典中存儲的這部分節點累加值。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeZeroSumSublists(self, head: ListNode) -> ListNode:
# 用一個值來存當前的累加和
# 再用一個字典來存累加和出現的次數
node = head
sums, zidian = 0, {}
while node:
sums += node.val
if sums ==0:
head = node.next
zidian.clear()
else:
if sums not in zidian:
zidian[sums] = node
else:
#zidian[sums].next = node.next
pre = zidian[sums]
sums2 = sums + pre.next.val
while sums2 != sums:
curr = zidian[sums2]
del zidian[sums2]
sums2 += curr.next.val
pre.next = node.next
#zidian[sums] = None
node = node.next
return head
86. 給定一個單鏈表和一個值x,把鏈表中小於x的節點放在前面,大於x的節點放到後面。
example:
Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
思路:和奇偶劃分類似,構造兩個新的節點。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
l1 = ListNode(0)
l2 = ListNode(0)
s1,s2 = l1, l2
while head:
if head.val<x:
l1.next = head
l1 = l1.next
else:
l2.next = head
l2 = l2.next
head = head.next
l2.next = None
l1.next = s2.next
return s1.next