One article to solve the classic algorithm problem of linked list

Posted by T2theC on Sat, 29 Jan 2022 20:39:08 +0100


Common questions about linked lists, such as:
Get the last k element, get the element in the middle position, judge whether there is a ring in the linked list, judge the length of the ring, and problems related to the length and position.

These problems can be solved through the flexible use of double pointers.

Sword finger Offer 22 The penultimate node in the lin k ed list

Input a linked list and output the penultimate node in the linked list.

In order to conform to the habit of most people, this question starts from 1, that is, the tail node of the linked list is the penultimate node.
For example, a linked list has six nodes. Starting from the beginning, their values are 1, 2, 3, 4, 5 and 6. The penultimate node of this linked list is the node with the value of 4.

Example:

Given a linked list: 1 - > 2 - > 3 - > 4 - > 5, and k = 2
Return to linked list 4 - > 5

Idea:
There are two pointers p and q, which initially point to the head node. First, let p move k times along next. At this time, p points to the k + 1st node, q points to the head node, and the distance between the two pointers is k. Then, move p and q at the same time until p points to null, at which time q points to the penultimate node. You can refer to the following figure to understand:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode p = head, q = head;
        while(k-->0){
            p = p.next;
        }
        while(p != null){
            p = p.next;
            q = q.next;
        }
        return q;
    }
}

Time complexity O(N): N is the length of the linked list.
Space complexity O(1): Double pointers P, Q use additional space of constant size.

Gets the element in the middle

Idea: there are two pointers fast and slow, which initially point to the head node. During each movement, fast moves backward twice and slow moves backward once until fast cannot move backward twice. This makes after each round of movement. The distance between fast and slow will increase by one. If the linked list has n elements, move up to n/2 rounds. When n is odd, slow just points to the middle node. When n is even, slow just points to the front of the two middle nodes.

The following code realizes that when n is an even number, the slow pointer points to the later of the two middle nodes, and when n is an odd number, the slow pointer points to the middle node.

class Solution {
public:
    ListNode middleNode(ListNode head) {
        ListNode p = head, q = head;
        while(q != null && q->next != null) {
            p = p.next;
            q = q.next.next;
        }
        return p;
    } 
};

141. Circular linked list

Given a linked list, judge whether there are links in the linked list. If the link exists in the list, it returns true. Otherwise, false is returned.

Example:

Idea: speed pointer
Define two pointers, one fast and one slow. The slow pointer moves only one step at a time, while the fast pointer moves two steps at a time. Initially, the slow pointer is at position head and the fast pointer is at position head next. In this way, if the two pointers meet in the process of moving, it means that the linked list is a ring linked list. Otherwise, the fast pointer will reach the end of the linked list, which is not a ring linked list.

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
 //If we initially place both pointers in head, the while loop will not execute.
 //Therefore, we can imagine a virtual node before the head. The slow pointer moves one step from the virtual node to the head,
 //The fast pointer moves two steps from the virtual node to the head Next, so we can use the while loop.
public class Solution {//Speed pointer
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}

Time complexity: O(N), where N is the number of nodes in the linked list.
When there is no ring in the linked list, the fast pointer will reach the tail of the linked list before the slow pointer, and each node in the linked list can be accessed at most twice. When there are links in the linked list, the distance between the fast and slow pointer will be reduced by one after each round of movement. The initial distance is the length of the ring, so at most N wheels can be moved.
Space complexity: O(1). We only used the extra space of two pointers.

Extension: if there is a ring, how to judge the length of the ring? The method is to continue moving after the fast and slow pointers meet until they meet for the second time. The number of moves between two encounters is the length of the ring.

160. Intersecting linked list

Here are the head nodes headA and headB of the two single linked lists. Please find and return the starting node where the two single linked lists intersect. If the two linked lists have no intersection, null is returned.

Note: there are no rings in the whole chain structure; After the function returns the result, the linked list must maintain its original structure.

Example:

Idea: if two linked lists intersect, the length after the intersection point is the same. What we need to do is to let the two linked lists traverse from the same distance at the end of the same distance. This position can only be the head node position of the short linked list.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {//Double pointer
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        ListNode pA = headA,pB = headB;
        while(pA != pB){
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

Time complexity: O(m+n), where m and N are the lengths of the linked list headA and headB respectively. Two pointers traverse two linked lists at the same time, and each pointer traverses two linked lists once each.
Space complexity: O(1).

206. Reverse linked list

Give you the head node of the single linked list. Please reverse the linked list and return the reversed linked list.

Example 1:

Input: head = [1,2,3,4,5]
Output: [5,4,3,2,1]

Example 2:

Input: head = []
Output: []

Method 1: iteration

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while(curr != null){
            ListNode nextTemp = curr.next;//Application of data structure linked list
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }
}

Time complexity: O(n), n is the length of the list.
Space complexity: O(1).

Method 2: recursion

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {//The next of n1 must point to ∅ 
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode p = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return p;
    }
}

Time complexity: O(n). Assuming that n is the length of the list, the time complexity is O(n).
Space complexity: O(n). Since recursion is used, implicit stack space will be used. The recursion depth may reach n layers.

reference resources:
Penultimate k-th graph
Intersecting linked list graph

Topics: data structure leetcode linked list