Day 17 – Advanced Linked Lists + Trapping Rainwater

Leetcode 237. Delete Node in a Linked List

Intuition: You’re given access to the node itself, not the head. Copy data from next node, then delete the next node.

void deleteNode(ListNode* node) {
    node->val = node->next->val;
    node->next = node->next->next;
}
// TC: O(1)
// SC: O(1)

Leetcode 160. Intersection of Two Linked Lists


1. Brute Force

Check each node of list A against all nodes of B.

  • TC: O(m * n)

  • SC: O(1)


2. Hashing

Store nodes of A in a set, then check B.

ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
    unordered_set<ListNode*> st;
    while (headA) st.insert(headA), headA = headA->next;
    while (headB) {
        if (st.count(headB)) return headB;
        headB = headB->next;
    }
    return NULL;
}
// TC: O(m + n)
// SC: O(m)

3. Two Pointers (Optimal)

Intuition: Equalize path length by switching heads after reaching null.

ListNode* getIntersectionNode(ListNode* a, ListNode* b) {
    ListNode* t1 = a, *t2 = b;
    while (t1 != t2) {
        t1 = t1 ? t1->next : b;
        t2 = t2 ? t2->next : a;
    }
    return t1;
}
// TC: O(m + n)
// SC: O(1)

Leetcode 141. Linked List Cycle


1. Hashing

bool hasCycle(ListNode* head) {
    unordered_set<ListNode*> seen;
    while (head) {
        if (seen.count(head)) return true;
        seen.insert(head);
        head = head->next;
    }
    return false;
}
// TC: O(n)
// SC: O(n)

2. Tortoise-Hare

bool hasCycle(ListNode *head) {
    if (!head || !head->next) return false;
    ListNode* slow = head, *fast = head;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) return true;
    }
    return false;
}
// TC: O(n)
// SC: O(1)

Leetcode 25. Reverse Nodes in K Group


1. Iterative

ListNode* reverseKGroup(ListNode* head, int k) {
    ListNode* curr = head;
    int count = 0;
    while (curr && count < k) {
        curr = curr->next;
        count++;
    }
    if (count < k) return head;

    curr = reverseKGroup(curr, k);
    while (count--) {
        ListNode* temp = head->next;
        head->next = curr;
        curr = head;
        head = temp;
    }
    return curr;
}
// TC: O(n)
// SC: O(n/k) -> recursion stack

Leetcode 234. Palindrome Linked List

Intuition: Use slow/fast pointer to find mid, reverse second half and compare.

bool isPalindrome(ListNode* head) {
    if (!head || !head->next) return true;
    ListNode* slow = head, *fast = head;
    while (fast->next && fast->next->next)
        slow = slow->next, fast = fast->next->next;

    ListNode* prev = NULL, *curr = slow->next;
    while (curr) {
        ListNode* next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }

    ListNode* first = head, *second = prev;
    while (second) {
        if (first->val != second->val) return false;
        first = first->next, second = second->next;
    }
    return true;
}
// TC: O(n)
// SC: O(1)

Leetcode 142. Linked List Cycle II

Intuition: Floyd's algo detects the cycle. Reset one pointer and move both at same speed.

ListNode *detectCycle(ListNode *head) {
    ListNode *slow = head, *fast = head;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            while (head != slow) {
                head = head->next;
                slow = slow->next;
            }
            return head;
        }
    }
    return NULL;
}
// TC: O(n)
// SC: O(1)

Flattening a Multilevel Linked List


Brute Force

Use array to store all values and recreate list.

  • TC: O(n)

  • SC: O(n)


Recursive Approach

Intuition: Merge next list into current level’s child list recursively.

Node* merge(Node* a, Node* b) {
    if (!a) return b;
    if (!b) return a;
    if (a->data < b->data) {
        a->bottom = merge(a->bottom, b);
        return a;
    } else {
        b->bottom = merge(a, b->bottom);
        return b;
    }
}

Node* flatten(Node* root) {
    if (!root || !root->next) return root;
    root->next = flatten(root->next);
    return merge(root, root->next);
}
// TC: O(N*M) (N: vertical lists, M: nodes in each)
// SC: O(1) (no extra list created)

Leetcode 61. Rotate Linked List


1. Brute Force

Rotate 1 step k times.

  • TC: O(k * n)

  • SC: O(1)


2. Optimal

Intuition: Connect last node to head to form a loop. Break it at the right point.

ListNode* rotateRight(ListNode* head, int k) {
    if (!head || !head->next || k == 0) return head;

    int len = 1;
    ListNode* tail = head;
    while (tail->next) {
        len++;
        tail = tail->next;
    }

    tail->next = head; // circular
    k = k % len;
    int steps = len - k;
    while (steps--) tail = tail->next;

    head = tail->next;
    tail->next = NULL;
    return head;
}
// TC: O(n)
// SC: O(1)

Leetcode 138. Copy List with Random Pointer


1. Using Map

Node* copyRandomList(Node* head) {
    unordered_map<Node*, Node*> mp;
    Node* temp = head;
    while (temp) {
        mp[temp] = new Node(temp->val);
        temp = temp->next;
    }
    temp = head;
    while (temp) {
        mp[temp]->next = mp[temp->next];
        mp[temp]->random = mp[temp->random];
        temp = temp->next;
    }
    return mp[head];
}
// TC: O(n)
// SC: O(n)

2. Optimal (No extra space)

Intuition: Clone nodes in between original ones, then fix randoms, then split.

Node* copyRandomList(Node* head) {
    if (!head) return NULL;

    Node* temp = head;
    while (temp) {
        Node* clone = new Node(temp->val);
        clone->next = temp->next;
        temp->next = clone;
        temp = clone->next;
    }

    temp = head;
    while (temp) {
        if (temp->random) temp->next->random = temp->random->next;
        temp = temp->next->next;
    }

    Node* dummy = new Node(0);
    Node* copy = dummy;
    temp = head;
    while (temp) {
        copy->next = temp->next;
        temp->next = temp->next->next;
        temp = temp->next;
        copy = copy->next;
    }
    return dummy->next;
}
// TC: O(n)
// SC: O(1)

Leetcode 42. Trapping Rainwater


1. Brute Force

For each index, calculate max on left and right.

  • TC: O(n²)

  • SC: O(1)


2. Prefix & Suffix Arrays

int trap(vector<int>& height) {
    int n = height.size();
    vector<int> left(n), right(n);
    left[0] = height[0];
    for (int i = 1; i < n; i++) left[i] = max(left[i - 1], height[i]);
    right[n - 1] = height[n - 1];
    for (int i = n - 2; i >= 0; i--) right[i] = max(right[i + 1], height[i]);

    int ans = 0;
    for (int i = 0; i < n; i++) {
        ans += min(left[i], right[i]) - height[i];
    }
    return ans;
}
// TC: O(n)
// SC: O(n)

3. Two Pointers

Intuition: Track leftMax and rightMax while iterating from both sides.

int trap(vector<int>& height) {
    int l = 0, r = height.size() - 1, leftMax = 0, rightMax = 0, ans = 0;
    while (l <= r) {
        if (height[l] <= height[r]) {
            leftMax = max(leftMax, height[l]);
            ans += leftMax - height[l++];
        } else {
            rightMax = max(rightMax, height[r]);
            ans += rightMax - height[r--];
        }
    }
    return ans;
}
// TC: O(n)
// SC: O(1)
0
Subscribe to my newsletter

Read articles from Siddharth Gandhi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Siddharth Gandhi
Siddharth Gandhi