Skip to content

Linked List

Table of Contents

21. Merge Two Sorted Lists

"""
-   Task: Merge the two linked lists into one sorted list.
"""

from typing import Optional

from leetpattern.utils import ListNode


# Linked List
def merge_two_lists(
    list1: Optional[ListNode], list2: Optional[ListNode]
) -> Optional[ListNode]:
    dummy = ListNode()
    cur = dummy

    while list1 and list2:
        if list1.val < list2.val:
            cur.next = list1
            list1 = list1.next
        else:
            cur.next = list2
            list2 = list2.next
        cur = cur.next

    if list1:
        cur.next = list1
    elif list2:
        cur.next = list2

    return dummy.next
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode* next) : val(x), next(next) {}
};

class Solution {
   public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode dummy;
        ListNode* cur = &dummy;

        while (list1 && list2) {
            if (list1->val < list2->val) {
                cur->next = list1;
                list1 = list1->next;
            } else {
                cur->next = list2;
                list2 = list2->next;
            }
            cur = cur->next;
        }

        cur->next = list1 ? list1 : list2;

        return dummy.next;
    }
};

int main() {
    Solution sol;
    ListNode* list1 = new ListNode(1, new ListNode(2, new ListNode(4)));
    ListNode* list2 = new ListNode(1, new ListNode(3, new ListNode(4)));
    ListNode* merged = sol.mergeTwoLists(list1, list2);
    vector<int> res;
    while (merged) {
        res.push_back(merged->val);
        merged = merged->next;
    }
    assert(res == std::vector<int>({1, 1, 2, 3, 4, 4}));
    return 0;
}

141. Linked List Cycle

from typing import Optional

from leetpattern.utils import LinkedList, ListNode


def has_cycle(head: Optional[ListNode]) -> bool:
    slow, fast = head, head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

        if slow == fast:
            return True

    return False


def test_has_cycle():
    ll = LinkedList([3, 2, 0, -4])
    ll.make_cycle(pos=1)
    assert has_cycle(ll.head)

    ll = LinkedList([1, 2])
    ll.make_cycle(pos=0)
    assert has_cycle(ll.head)

    ll = LinkedList([1, 2, 3, 4, 5])
    assert not has_cycle(ll.head)
#include <cassert>
#include <iostream>

#include "include/lists.hpp"
using namespace std;

class Solution {
   public:
    bool has_cycle(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;

        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;

            if (fast == slow) return true;
        }
        return false;
    }
};

int main() {
    Solution solution;

    ListNode* head = LinkedList::build({3, 2, 0, -4});
    // create cycle
    head = LinkedList::make_cycle(head, 1);
    assert(solution.has_cycle(head) == true);
    // no cycle
    ListNode* head2 = LinkedList::build({1, 2});
    assert(solution.has_cycle(head2) == false);
    // no cycle
    ListNode* head3 = LinkedList::build({1});
    assert(solution.has_cycle(head3) == false);

    return 0;
}

206. Reverse Linked List

from typing import Optional

from leetpattern.utils import LinkedList, ListNode


def reverse_list_iterative(head: Optional[ListNode]) -> Optional[ListNode]:
    cur = head
    prev = None

    while cur:
        temp = cur.next
        cur.next = prev

        prev = cur
        cur = temp

    return prev


def reverse_list_recursive(head: Optional[ListNode]) -> Optional[ListNode]:
    def reverse(cur, prev):
        if not cur:
            return prev

        temp = cur.next
        cur.next = prev
        return reverse(temp, cur)

    return reverse(head, None)


def test_reverse_list():
    ll = LinkedList([1, 2, 3, 4, 5])
    ll.head = reverse_list_iterative(ll.head)
    assert ll.to_array() == [5, 4, 3, 2, 1]

    ll = LinkedList([1, 2, 3, 4, 5])
    ll.head = reverse_list_recursive(ll.head)
    assert ll.to_array() == [5, 4, 3, 2, 1]

876. Middle of the Linked List

from typing import Optional

from leetpattern.utils import LinkedList, ListNode


# Linked List
def middleNode(head: Optional[ListNode]) -> Optional[ListNode]:
    fast, slow = head, head

    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next

    return slow


def test_middleNode():
    # Test case 1: Odd number of nodes
    ll1 = LinkedList([1, 2, 3, 4, 5])
    middle1 = middleNode(ll1.head)
    result1 = LinkedList(middle1)
    assert result1.to_array() == [3, 4, 5]

    # Test case 2: Even number of nodes
    ll2 = LinkedList([1, 2, 3, 4, 5, 6])
    middle2 = middleNode(ll2.head)
    result2 = LinkedList(middle2)
    assert result2.to_array() == [4, 5, 6]

    # Test case 3: Single node
    ll3 = LinkedList([1])
    middle3 = middleNode(ll3.head)
    result3 = LinkedList(middle3)
    assert result3.to_array() == [1]

    # Test case 4: Two nodes
    ll4 = LinkedList([1, 2])
    middle4 = middleNode(ll4.head)
    result4 = LinkedList(middle4)
    assert result4.to_array() == [2]

146. LRU Cache

  • LeetCode | 力扣

  • Tags: Hash Table, Linked List, Design, Doubly Linked List

"""
- Design and implement a data structure for **Least Recently Used (LRU) cache**. It should support the following operations: get and put.
- [lru](https://media.geeksforgeeks.org/wp-content/uploads/20240909142802/Working-of-LRU-Cache-copy-2.webp)
- ![146](https://miro.medium.com/v2/resize:fit:650/0*fOwBd3z0XtHh7WN1.png)

| Data structure     | Description                   |
| ------------------ | ----------------------------- |
| Doubly Linked List | To store the key-value pairs. |
| Hash Map           | To store the key-node pairs.  |
"""

from collections import OrderedDict


# Doubly Linked List
class Node:
    def __init__(self, key=0, val=0):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None


class LRUCache:

    def __init__(self, capacity: int):
        self.cap = capacity
        self.cache = {}
        self.head = Node()
        self.tail = Node()
        self.head.next = self.tail
        self.tail.prev = self.head

    def remove(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def add_to_last(self, node):
        self.tail.prev.next = node
        node.prev = self.tail.prev
        node.next = self.tail
        self.tail.prev = node

    def move_to_last(self, node):
        self.remove(node)
        self.add_to_last(node)

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        node = self.cache[key]
        self.move_to_last(node)
        return node.val

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            node = self.cache[key]
            node.val = value
            self.move_to_last(node)
            return None

        if len(self.cache) == self.cap:
            del self.cache[self.head.next.key]
            self.remove(self.head.next)

        node = Node(key=key, val=value)
        self.cache[key] = node
        self.add_to_last(node)


# OrderedDict
class LRUCacheOrderedDict:
    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.cap = capacity

    def get(self, key: int):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key, last=True)
        return self.cache[key]

    def put(self, key: int, value: int):
        if key in self.cache:
            self.cache.move_to_end(key, last=True)
        elif len(self.cache) >= self.cap:
            self.cache.popitem(last=False)

        self.cache[key] = value


cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
assert cache.get(1) == 1
cache.put(3, 3)
assert cache.get(2) == -1
cache.put(4, 4)
assert cache.get(1) == -1
assert cache.get(3) == 3
assert cache.get(4) == 4


cache = LRUCacheOrderedDict(2)
cache.put(1, 1)
cache.put(2, 2)
assert cache.get(1) == 1
cache.put(3, 3)
assert cache.get(2) == -1
cache.put(4, 4)
assert cache.get(1) == -1
assert cache.get(3) == 3
assert cache.get(4) == 4

print("LRU Cache passed")
print("LRU Cache Ordered Dict passed")
#include <cassert>
#include <iostream>
#include <unordered_map>
using namespace std;

class Node {
   public:
    int key;
    int val;
    Node *prev;
    Node *next;

    Node(int k = 0, int v = 0) : key(k), val(v), prev(nullptr), next(nullptr) {}
};

class LRUCache {
   private:
    int cap;
    unordered_map<int, Node *> cache;
    Node *head;
    Node *tail;

    void remove(Node *node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void insert_to_last(Node *node) {
        tail->prev->next = node;
        node->prev = tail->prev;
        tail->prev = node;
        node->next = tail;
    }

    void move_to_last(Node *node) {
        remove(node);
        insert_to_last(node);
    }

   public:
    LRUCache(int capacity) {
        this->cap = capacity;
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
    }

    int get(int key) {
        if (cache.find(key) != cache.end()) {
            Node *node = cache[key];
            move_to_last(node);
            return node->val;
        }
        return -1;
    }

    void put(int key, int value) {
        if (cache.find(key) != cache.end()) {
            Node *node = cache[key];
            node->val = value;
            move_to_last(node);
        } else {
            Node *newNode = new Node(key, value);
            cache[key] = newNode;
            insert_to_last(newNode);

            if ((int)cache.size() > cap) {
                Node *removed = head->next;
                remove(removed);
                cache.erase(removed->key);
                delete removed;
            }
        }
    }
};

int main() {
    LRUCache lru(2);
    lru.put(1, 1);
    lru.put(2, 2);
    assert(lru.get(1) == 1);   // returns 1
    lru.put(3, 3);             // evicts key 2
    assert(lru.get(2) == -1);  // returns -1 (not found)
    lru.put(4, 4);             // evicts key 1
    assert(lru.get(1) == -1);  // returns -1 (not found)
    assert(lru.get(3) == 3);   // returns 3
    assert(lru.get(4) == 4);   // returns 4
    return 0;
}