[Leetcode] [Tutorial] Linked list


# 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

Guess you like

Origin blog.csdn.net/weixin_45427144/article/details/131475282