Article directory
- 2. Add two numbers
- 21. Merge two ordered linked lists
- 19. Delete the Nth node from the bottom of the linked list
- 160. Intersected linked lists
- 142. Circular Linked List II
- 206. Reverse linked list
- 234. Palindrome linked list
- 24. Exchange nodes in the linked list pairwise
- 25. A set of K flip linked lists
- 328. Odd-even linked list
- 138. Copy linked list with random pointers
- 148. Sorted linked list
- 23. Merge K ascending linked lists
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
2. Add two numbers
You are given two non-empty linked lists, representing two non-negative integers. Each of their digits is stored in reverse order, and each node can only store one digit.
Please add two numbers and return a linked list representing the sum in the same form.
You can assume that except for the number 0, neither number will start with a 0.
Example:
Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807.
Solution
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(-1)
curr = dummy
carry = 0
while l1 or l2:
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
carry, val = divmod(val1 + val2 + carry, 10)
curr.next = ListNode(val)
if l1:
l1 = l1.next
if l2:
l2 = l2.next
curr = curr.next
if carry:
curr.next = ListNode(carry)
return dummy.next
21. Merge two ordered linked lists
Merge the two ascending linked lists into a new ascending linked list and return. The new linked list is formed by concatenating all the nodes of the two given linked lists.
Example:
Input: l1 = [1,2,4], l2 = [1,3,4]
Output: [1,1,2,3,4,4]
Solution
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(-1)
curr = dummy
while list1 and list2:
if list1.val <= list2.val:
curr.next = list1
list1 = list1.next
else:
curr.next = list2
list2 = list2.next
curr = curr.next
if list1:
curr.next = list1
elif list2:
curr.next = list2
return dummy.next
19. Delete the Nth node from the bottom of the linked list
Give you a linked list, delete the nth node from the bottom of the linked list, and return the head node of the linked list.
Example:
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Solution
We can use the stack to solve this problem. Each node in the linked list is pushed onto the stack, and then we pop it out of the stack n times. Then the top element of the stack is the node before the node we want to delete. This is because the stack is a last-in-first-out data structure, and the order in which items are popped out of the stack is exactly the opposite of the order in which they are pushed into the stack. Therefore, the order in which items are popped out of the stack is the reverse order of the linked list nodes.
After deleting the node, we also need to consider a problem. If we delete the head node, then we need to re-determine the new head node. To solve this problem, we can add a dumb node at the beginning.
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
stack = []
cur = dummy
while cur:
stack.append(cur)
cur = cur.next
for _ in range(n):
stack.pop()
prev = stack[-1]
prev.next = prev.next.next
return dummy.next
We can also use the fast and slow pointer method to solve this problem. Maintain two pointers, one pointer advances n steps first, and then both pointers advance simultaneously until the fast pointer reaches the end of the linked list. At this time, the slow pointer points to the node we need to delete.
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
fast = slow = dummy
# 快指针先前进n步
for _ in range(n + 1):
fast = fast.next
# 快慢指针同时前进,直到快指针到达链表末尾
while fast:
fast = fast.next
slow = slow.next
# 删除慢指针的下一个节点
slow.next = slow.next.next
return dummy.next
160. Intersected linked lists
Given the head nodes headA and headB of two singly linked lists, please find and return the starting node where the two singly linked lists intersect. If there is no intersecting node between the two linked lists, null is returned.
The figure shows that two linked lists begin to intersect at node c1:
The question data ensures that there are no loops in the entire chain structure.
Example:
Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
Output: Intersected at '8'
Solution
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
if not headA or not headB:
return None
nodeA, nodeB = headA, headB
while nodeA != nodeB:
nodeA = nodeA.next if nodeA else headB
nodeB = nodeB.next if nodeB else headA
return nodeA
142. Circular Linked List II
Given the head node head of a linked list, return the first node where the linked list begins to enter the loop. If the linked list is loop-free, null is returned.
If there is a node in the linked list that can be reached again by continuously tracking the next pointer, then there is a cycle in the linked list. In order to represent a ring in a given linked list, the evaluation system internally uses the integer pos to represent the position in the linked list where the tail of the linked list is connected (index starts from 0). If pos is -1, there is no cycle in the linked list. Note: pos is not passed as a parameter, it is only used to identify the actual situation of the linked list.
Modification of the linked list is not allowed.
Example:
Input: head = [3,2,0,-4], pos = 1
Output: Return the linked list node with index 1
Explanation: There is a ring in the linked list, and its tail is connected to the second node.
Solution
Assume that the distance from the starting position of the linked list to the starting position of the ring is A, the distance from the starting position of the ring to the position where the slow pointer and the fast pointer first meet is B, and the length of the ring is C.
When the slow pointer and the fast pointer meet for the first time, the slow pointer has gone A+B steps, and the fast pointer has gone A+B+kC steps, where k is the number of turns the fast pointer has taken in the ring.
Since the fast pointer is twice as fast as the slow pointer, 2(A+B) = A+B+kC. From this equation, we can derive A = kC - B. This means that the distance from the head node to the starting node of the ring is the same as the distance from the first encounter position to the starting position of the ring.
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return None
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if not fast or not fast.next:
return None
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
206. Reverse linked list
Given the head node of a singly linked list, please reverse the linked list and return the reversed linked list.
Example:
Input: head = [1,2,3,4,5]
Output: [5,4,3,2,1]
Solution
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head:
return None
prev = None
curr = head
while curr:
temp_next = curr.next
curr.next = prev
prev = curr
curr = temp_next
return prev
In this code, we first check whether head is None or whether head.next is None. If so, we return head directly, because empty linked lists or linked lists with only one element are still themselves after inversion.
Next, we initialize prev to None, curr to head, and start the loop. If prev is head and curr is head.next, the next attribute of the head node of the original linked list (that is, the tail node of the reversed linked list) will still point to the second node of the original linked list.
234. Palindrome linked list
Given the head node of a singly linked list, please determine whether the linked list is a palindrome linked list. If so, returns true; otherwise, returns false.
Example:
Input: head = [1,2,2,1]
Output: true
Solution
A straightforward method is to use a stack to store each element of the linked list, and then compare whether the popping sequence of the stack and the traversing sequence of the linked list are the same.
To determine whether a linked list is a palindrome, we can first find the midpoint of the linked list, then reverse the second half of the linked list, and then compare whether the first half and the second half of the linked list are the same. If they are the same, then the linked list is palindrome.
We can use the fast and slow pointer method to find the midpoint of the linked list. We create two pointers, a fast pointer that moves two steps at a time, and a slow pointer that moves one step at a time. When the fast pointer reaches the end of the linked list, the slow pointer is at the midpoint of the linked list.
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
if not head or not head.next:
return True
# Find the middle of the list
slow, fast = head, head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
# Reverse the second half of the list
prev, curr = None, slow.next
while curr:
next_temp = curr.next
curr.next = prev
prev = curr
curr = next_temp
# Compare the first half and the second half
first, second = head, prev
while second:
if first.val != second.val:
return False
first = first.next
second = second.next
return True
24. Exchange nodes in the linked list pairwise
Give you a linked list, swap adjacent nodes in pairs, and return the head node of the swapped list. You must complete this question without modifying the values inside the nodes (i.e., only node swaps can be performed).
Example:
Input: head = [1,2,3,4]
Output: [2,1,4,3]
Solution
The nodes in the linked list can be exchanged recursively.
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
# nodes in current pair
first_node = head
second_node = head.next
# swapping
first_node.next = self.swapPairs(second_node.next)
second_node.next = first_node
# now head is second node
return second_node
We can also use an iterative approach to solve this problem.
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
dummy = ListNode(-1)
dummy.next = head
prev = dummy
while prev.next and prev.next.next:
first_node = prev.next
second_node = prev.next.next
prev.next = second_node
first_node.next = second_node.next
second_node.next = first_node
prev = first_node
return dummy.next
25. A set of K flip linked lists
Give you the head node of the linked list, flip every k nodes in a group, please return the modified linked list.
k is a positive integer whose value is less than or equal to the length of the linked list. If the total number of nodes is not an integer multiple of k, then please keep the last remaining nodes in their original order.
You can't just change the value inside the node, you need to actually swap the node.
Example:
Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]
Solution
In the "pairwise exchange" problem, we exchanged two nodes as a group, but now we exchange k nodes as a group. Therefore, we can refer to the recursive solution of the "pairwise exchange" problem to solve this problem.
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
# 首先,我们需要检查是否有足够的k个节点可以反转
count = 0
node = head
while node and count < k:
node = node.next
count += 1
if count < k:
return head
prev = None
curr = head
for _ in range(k):
temp_next = curr.next
curr.next = prev
prev = curr
curr = temp_next
# 然后我们递归地反转剩下的节点,并将反转后的节点连接到已经反转的节点上
head.next = self.reverseKGroup(curr, k)
# 最后我们返回反转后的头节点
return prev
We can also use an iterative approach to solve this problem.
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
groupPrev = dummy
while True:
kth = self.getKth(groupPrev, k)
if not kth:
break
groupNext = kth.next
prev, curr = kth.next, groupPrev.next
while curr != groupNext:
nextNode = curr.next
curr.next = prev
prev, curr = curr, nextNode
nextGroupPrev = groupPrev.next
groupPrev.next = prev
groupPrev = nextGroupPrev
return dummy.next
def getKth(self, curr: ListNode, k: int) -> ListNode:
while curr and k > 0:
curr = curr.next
k -= 1
return curr
328. Odd-even linked list
Given the head node of a singly linked list, combine all nodes with odd indexes and nodes with even indexes together, and return the reordered list.
The index of the first node is considered an odd number, the index of the second node is an even number, and so on.
Please note that the relative order within the even and odd groups should remain consistent with the input.
Example:
Input: head = [1,2,3,4,5]
Output: [1,3,5,2,4]
Solution
138. Copy linked list with random pointers
You are given a linked list of length n. Each node contains an additional random pointer random, which can point to any node or an empty node in the linked list.
Construct a deep copy of this linked list. A deep copy should consist of exactly n completely new nodes, where the value of each new node is set to the value of its corresponding original node. The next pointer and random pointer of the new node should also point to the new node in the copied linked list, so that these pointers in the original linked list and the copied linked list can represent the same linked list state. No pointer in the copied linked list should point to a node in the original linked list.
For example, if there are two nodes X and Y in the original linked list, X.random --> Y. Then the corresponding two nodes x and y in the copy linked list also have x.random --> y.
Returns the head node of the copy linked list.
Represent the linked list in input/output as a linked list consisting of n nodes. Each node is represented by a [val, random_index]:
val: an integer representing Node.val.
random_index: The node index pointed to by the random pointer (ranging from 0 to n-1); null if it does not point to any node.
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
Example:
Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0], [11,4],[10,2],[1,0]]
Solution
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
if not head:
return None
mapping = {
}
curr = head
while curr:
mapping[curr] = Node(curr.val)
curr = curr.next
curr = head
while curr:
if curr.next:
mapping[curr].next = mapping[curr.next]
if curr.random:
mapping[curr].random = mapping[curr.random]
curr = curr.next
return mapping[head]
There is a more clever and efficient way to solve this problem, which is to insert a copy of it directly after each node of the original linked list, then repair the random pointer, and finally split the original linked list and the copied linked list.
148. Sorted linked list
Given the head node of the linked list, please sort it in ascending order and return the sorted linked list.
Solution
We can apply the idea of merge sort to the linked list sorting problem:
-
Split: We need to find the midpoint of the linked list and split the linked list into two smaller linked lists. This can be achieved through the method of fast and slow pointers. The fast pointer moves two steps at a time, and the slow pointer moves one step at a time. When the fast pointer reaches the end of the linked list, the slow pointer is exactly at the midpoint of the linked list.
-
Recursive sorting: We regard the two split linked lists as two new linked lists to be sorted, and sort them separately. This process is recursive. The basic situation of recursion is that the length of the linked list is 1 (only one element) or 0 (no elements). At this time, the linked list is already in order and can be returned directly.
-
Merge: We need to merge two sorted linked lists into one large ordered linked list. The merging process can also be regarded as a double pointer operation: we compare the head nodes of the two linked lists each time, add the smaller node to the result linked list, and then move the corresponding pointer forward one step until the two linked lists All nodes in have been processed.
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
# Base case
if not head or not head.next:
return head
# Split the list into two halves
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# Recursively sort the two halves
second = slow.next
slow.next = None
left = self.sortList(head)
right = self.sortList(second)
# Merge the sorted halves
return self.merge(left, right)
def merge(self, left, right):
dummy = ListNode(0)
curr = dummy
while left and right:
if left.val < right.val:
curr.next = left
left = left.next
else:
curr.next = right
right = right.next
curr = curr.next
curr.next = left if left else right
return dummy.next
23. Merge K ascending linked lists
You are given an array of linked lists, each linked list has been sorted in ascending order.
Please merge all linked lists into an ascending linked list and return the merged linked list.
Solution
Merge sort gives us a revelation: we can first divide k linked lists into two groups, each group contains k/2 linked lists, and then merge each group, so that we can put a The problem is decomposed into two problems of size k/2. We can then continue to further decompose each group until each group contains only one linked list, at which point the merge operation can be performed directly.
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if not lists:
return None
elif len(lists) == 1:
return lists[0]
mid = len(lists) // 2
left = self.mergeKLists(lists[:mid])
right = self.mergeKLists(lists[mid:])
return self.mergeTwoLists(left, right)
def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0)
curr = dummy
while l1 and l2:
if l1.val < l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
curr.next = l1 if l1 else l2
return dummy.next