# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
Solution:
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head:
return head
curr = head
while curr.next:
cv = curr.val
nv = curr.next.val
if cv == nv:
_next = curr.next
nn_of_c = _next.next
curr.next = nn_of_c
del _next
else:
if not curr.next: # [..., 5, 9, 9, 9] case
break
curr = curr.next
return head
Solution:
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head:
return None
dummy_head = ListNode(None)
dummy_head.next = head
curr = dummy_head
dup_v = None
while curr:
# cv = curr.val
_next = curr.next
if _next and _next.next:
nv = _next.val
nnv = _next.next.val
else:
break
dup_v = nv if nv == nnv else None
# remove duplicate node
if dup_v is not None: # [0, 0, 0, 0]
while _next:
if _next.val == dup_v:
curr.next = _next.next
del _next
_next = curr.next
else:
break
else:
curr = _next
return dummy_head.next
Solution:
使用 dummy node 的 solution 只要正确编写都不需要判断 head 是否为空。
class Solution:
def rm_elems(self, head, key):
dummy_head = ListNode(key-1)
dummy_head.next = head
curr = dummy_head
while curr: # make sure value of current node != key
if curr.next:
if curr.next.val == key:
rm_node = curr.next
curr.next = rm_node.next
del rm_node
else:
curr = curr.next
else:
break
return dummy_head.next
def removeElements(self, head: ListNode, val: int) -> ListNode:
return self.rm_elems(head, val)
灵活使用 dummy head 的思想,确保当前节点位置不用删除!
这一点应该是这个 solution 比大部分人都快的原因!
Solution:
这道题在我看过之前还在面试的时候被问过。当时觉得很懵逼 ?。
后来没过几天就刚好在《剑指 Offer》看到这个问题,才搞懂面试官的意思。
或许是太过奇淫技巧,所以这个问题被踩的次数远远大于点赞的次数,思路还是很不错的。
可以学学扩展扩展思维,但是实际上不太可能这么操作 – 毕竟尾节点你都没办法删掉!
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
if node is None:
return
if node.next: # not tail node
_next = node.next
node.val = _next.val
node.next = _next.next
del _next
return
return
Solution:
分析实现过程见注释。
class Solution:
def recursive_reverse_list(self, dst, src):
# 8->None => 8->None
# 3->7->None => 7->3->None
# 3 \ 7->None
# 7->3 \ None
# None? tail->None, return head
# 1->2->3->4->None
# 1 \ 2->3->4->None
# 2->1 \ 3->4->None
# 3->2->1 \ 4->None
# 4->3->2->1 \ None
# None? tail->None, return head
if src is None: # head
return dst
else:
n_src = src.next
src.next = dst
return self.recursive_reverse_list(src, n_src)
def iterative_reverse_list(self, node):
pass
def reverseList(self, head: ListNode) -> ListNode:
if not head:
return None
if not head.next:
return head
if True:
tail = head
new_head = self.recursive_reverse_list(head, head.next)
tail.next = None
else:
new_head = self.iterative_reverse_list(head)
return new_head
Solution:
这道题在上一道题之后做的。而且上一道题昨天做的,竟然给忘记了。
原本还是一开始就想用递归实现,但是因为忘了昨天实现过了整个链表翻转,倒是一时没想出来,最后只能使用其它方案实现。
如果早点发现原来实现过的链表翻转,那么在原来的基础上修改应该是可以比较快实现的。
不过总的来说,在纸上画画两下之后,还是满足了 Do it in one-pass.
class Solution:
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
new_head = None
new_tail = None
dummy_head = ListNode(None)
dummy_head.next = head
curr = dummy_head
# find new_head by use m
for _ in range(m-1):
curr = curr.next
tail_of_head = curr
new_head = curr.next
curr = curr.next # move to 'n'-place
# find new_tail by use n
for _ in range(n-m): # take care about border
curr = curr.next
new_tail = curr.next # curr at 'm'-place
curr.next = None
# reverse
while new_head:
head = new_head
new_head = new_head.next
head.next = new_tail
new_tail = head
tail_of_head.next = new_tail
return dummy_head.next
Solution:
Approach 2: Two Pointers
Intuition
Imagine two runners running on a track at different speed. What happens when the track is actually a circle?
Algorithm
The space complexity can be reduced to O(1)O(1) by considering two pointers at different speed - a slow pointer and a fast pointer. The slow pointer moves one step at a time while the fast pointer moves two steps at a time.
If there is no cycle in the list, the fast pointer will eventually reach the end and we can return false in this case.
Now consider a cyclic list and imagine the slow and fast pointers are two runners racing around a circle track. The fast runner will eventually meet the slow runner. Why? Consider this case (we name it case A) - The fast runner is just one step behind the slow runner. In the next iteration, they both increment one and two steps respectively and meet each other.
How about other cases? For example, we have not considered cases where the fast runner is two or three steps behind the slow runner yet. This is simple, because in the next or next’s next iteration, this case will be reduced to case A mentioned above.
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast or not fast.next:
return False
slow = slow.next
fast = fast.next.next
return True
Complexity analysis
Time complexity : O ( n ) O(n) O(n). Let us denote nn as the total number of nodes in the linked list. To analyze its time complexity, we consider the following two cases separately.
List has no cycle:
The fast pointer reaches the end first and the run time depends on the list’s length, which is O ( n ) O(n) O(n).
List has a cycle:
We break down the movement of the slow pointer into two steps, the non-cyclic part and the cyclic part:
Therefore, the worst case time complexity is O ( N + K ) O(N+K) O(N+K), which is O ( n ) O(n) O(n).
Space complexity : O ( 1 ) O(1) O(1). We only use two nodes (slow and fast) so the space complexity is O ( 1 ) O(1) O(1).
Solution:
先做过 189. Rotate Array 的话,对于比如 k > 链表长度 k > \text{链表长度} k>链表长度 这样的情况会提前考虑到!所以首先遍历一遍链表获取长度。
因为链表不需要像数组那样一个一个移动值,所以在这个问题上,链表的操作理论上会更快。
class Solution:
def get_len(self, head):
_len = 0
curr = head
while curr:
_len += 1
curr = curr.next
return _len
def rot_k(self, head, k):
if k == 0:
return head
p_slow = head
p_fast = head
for _ in range(k):
p_fast = p_fast.next
while p_fast.next:
p_fast = p_fast.next
p_slow = p_slow.next
p_fast.next = head
new_head = p_slow.next
p_slow.next = None
return new_head
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if head == None:
return None
k = k % self.get_len(head)
return self.rot_k(head, k)
Given a non-empty, singly linked list with head node head, return a middle node of linked list.
If there are two middle nodes, return the second middle node.
Example 1:
Input: [1,2,3,4,5]
Output: Node 3 from this list (Serialization: [3,4,5])
The returned node has value 3. (The judge’s serialization of this node is [3,4,5]).
Note that we returned a ListNode object ans, such that:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL.
Example 2:
Input: [1,2,3,4,5,6]
Output: Node 4 from this list (Serialization: [4,5,6])
Since the list has two middle nodes with values 3 and 4, we return the second one.
Solution:
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
if head.next is None:
return head
# O-head-one-> O-two-> None
fast = head.next.next
slow = head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow