Showing posts with label Linked List. Show all posts
Showing posts with label Linked List. Show all posts

Wednesday, April 10, 2024

[LeetCode] Merge Two Sorted Lists

Problem: Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

Example:

Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]
Input: list1 = [], list2 = []
Output: []
Input: list1 = [], list2 = [0]
Output: [0]


Approach: We have already done it recursively before. Just adding the iterative approach here. You can just understand it by looking at the implementation.


Implementation in C#:

    public ListNode MergeTwoLists(ListNode list1, ListNode list2)
    {
        var itr1 = list1;
        var itr2 = list2;
        var head = new ListNode();
        var itr = head;

        while (itr1 != null && itr2 != null)
        {
            if (itr1.val <= itr2.val)
            {
                itr.next = itr1;
                itr1 = itr1.next;
            }
            else
            {
                itr.next = itr2;
                itr2 = itr2.next;
            }
            itr = itr.next;
        }
        if (itr1 != null)
        {
            itr.next = itr1;
        }
        else if (itr2 != null)
        {
            itr.next = itr2;
        }
        return head.next;
    }


Complexity: O(Max(m, n))

Middle of the Linked List

Problem: Given the head of a singly linked list, return the middle node of the linked list.

If there are two middle nodes, return the second middle node.

Example:

Input: head = [1,2,3,4,5]
Output: [3,4,5]
Explanation: The middle node of the list is node 3.

Input: head = [1,2,3,4,5,6]
Output: [4,5,6]
Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one.


Approach: We can do it in two passes where in first iteration we can get the number of nodes and in the second iteration we will get the size/2th node. 

Let's try to reduce the number of iterations. What we can do, we can use two pointers; one is slow which goes 1 step at one time and one is fast which goes 2 steps at a time:

  • slow = slow.next
  • fast = fast.next.next

Obviously when fast is null or it's next is null, slow will be pointing at the middle node. That's all!


Implementation in C#:

    public ListNode MiddleNode(ListNode head)
    {
        if (head == null || head.next == null)
        {
            return head;
        }
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null)
        {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

Complexity: O(n)

Friday, March 8, 2024

[LeetCode] Maximum Twin Sum of a Linked List

Problem: In a linked list of size n, where n is even, the ith node (0-indexed) of the linked list is known as the twin of the (n-1-i)th node, if 0 <= i <= (n / 2) - 1.

  • For example, if n = 4, then node 0 is the twin of node 3, and node 1 is the twin of node 2. These are the only nodes with twins for n = 4.

The twin sum is defined as the sum of a node and its twin.

Given the head of a linked list with even length, return the maximum twin sum of the linked list.

Example:

Input: head = [5,4,2,1]
Output: 6
Explanation:
Nodes 0 and 1 are the twins of nodes 3 and 2, respectively. All have twin sum = 6.
There are no other nodes with twins in the linked list.
Thus, the maximum twin sum of the linked list is 6. 

Input: head = [4,2,2,3]
Output: 7
Explanation:
The nodes with twins present in this linked list are:
- Node 0 is the twin of node 3 having a twin sum of 4 + 3 = 7.
- Node 1 is the twin of node 2 having a twin sum of 2 + 2 = 4.
Thus, the maximum twin sum of the linked list is max(7, 4) = 7. 

Input: head = [1,100000]
Output: 100001
Explanation:
There is only one node with a twin in the linked list having twin sum of 1 + 100000 = 100001.


Constraints:

  • The number of nodes in the list is an even integer in the range [2, 10^5].
  • 1 <= Node.val <= 10^5


Approach: First approach which comes to mind is just to get to the middle node of the list, reverse the list from there and then using two pointers one from the head say first and one from the middle node say second, keep comparing the sum of the nodes till the second pointer is not null. In the end return the maximum of such sum.

The above approach will work but what if we can't modify the input list? We can obviously make a copy of the linked list and apply the above approach but this could be expensive the list nodes are heavy and will be more space intensive. 

We can also use stack here. We can initially push all the nodes into stack. Now again we will take two pointers where one starts from head say first and another starts from top of the stack say second. We will keep comparing the sum of these nodes till next of first is not second. In the end we can return the maximum of all these sums.


Implementation in C#:

By modifying the input list:

    public int PairSum(ListNode head)
    {
        if (head == null)
        {
            return 0;
        }
        ListNode mid = this.GetMidNode(head);
        ListNode reverseHead = this.ReverseList(mid);
        ListNode first = head, second = reverseHead;
        int maxSum = 0;
        while(second != null)
        {
            maxSum = Math.Max(maxSum, first.val + second.val);
            first = first.next;
            second = second.next;
        }
        return maxSum;
    }

    private ListNode GetMidNode(ListNode head)
    {
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null)
        {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    private ListNode ReverseList(ListNode head)
    {
        if (head == null || head.next == null)
        {
            return head;
        }
        ListNode prev = null, curr = head;
        while(curr != null)
        {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }


Using stack:

    public int PairSum(ListNode head)
    {
        if (head == null)
        {
            return 0;
        }
        var stack= new Stack<ListNode>();
        this.PushNodesToStack(head, stack);
        int maxSum = 0;
        ListNode fwd = head, rev = stack.Pop();
        while (fwd.next != rev)
        {
            if (maxSum < fwd.val + rev.val)
            {
                maxSum = fwd.val + rev.val;
            }
            fwd = fwd.next;
            rev = stack.Pop();
        }
        if (maxSum < fwd.val + rev.val)
        {
            maxSum = fwd.val + rev.val;
        }
        return maxSum;
    }

    private void PushNodesToStack(ListNode head, Stack<ListNode> stack)
    {
        for (ListNode node = head; node != null; node = node.next)
        {
            stack.Push(node);
        }
    }


Complexity: O(n)

[LeetCode] Delete the Middle Node of a Linked List

Problem: You are given the head of a linked list. Delete the middle node, and return the head of the modified linked list.

The middle node of a linked list of size n is the [n / 2]th node from the start using 0-based indexing, where [x] denotes the largest integer less than or equal to x.

For n = 1, 2, 3, 4, and 5, the middle nodes are 0, 1, 1, 2, and 2, respectively.

Example:

Input: head = [1,3,4,7,1,2,6]
Output: [1,3,4,1,2,6]
Explanation:
The above figure represents the given linked list. The indices of the nodes are written below.
Since n = 7, node 3 with value 7 is the middle node, which is marked in red.
We return the new list after removing this node. 

Input: head = [1,2,3,4]
Output: [1,2,4]
Explanation:
The above figure represents the given linked list.
For n = 4, node 2 with value 3 is the middle node, which is marked in red.

Input: head = [2,1]
Output: [2]
Explanation:
The above figure represents the given linked list.
For n = 2, node 1 with value 1 is the middle node, which is marked in red.
Node 0 with value 2 is the only node remaining after removing node 1.


Approach: The approach is simple. We are going to use fast and slow pointer appoach. The only problem is if slow is at middle, how we are going to delete it. We can maintain a prev pointer or we can make fast start from head.next.next. 

That's all!


Implementation in C#:

With prev node:

    public ListNode DeleteMiddle1(ListNode head)
    {
        if (head == null || head.next == null)
        {
            return null;
        }
        ListNode prev = null, slow = head, fast = head;
        while (fast != null && fast.next != null)
        {
            prev = slow;
            slow = slow.next;
            fast = fast.next;
            if (fast != null)
            {
                fast = fast.next;
            }
        }
        prev.next = prev.next.next;
        return head;
    }

Without prev node:

    public ListNode DeleteMiddle(ListNode head)
    {
        if (head == null || head.next == null)
        {
            return null;
        }
        ListNode slow = head, fast = head.next.next;
        while (fast != null && fast.next != null)
        {
            slow = slow.next;
            fast = fast.next;
            if (fast != null)
            {
                fast = fast.next;
            }
        }
        slow.next = slow.next.next;
        return head;
    }

Complexity: O(n)

Thursday, October 21, 2021

[LeetCode] Design HashSet

Problem: Design a HashSet without using any built-in hash table libraries.

Implement MyHashSet class:

  • void add(key) Inserts the value key into the HashSet.
  • bool contains(key) Returns whether the value key exists in the HashSet or not.
  • void remove(key) Removes the value key in the HashSet. If key does not exist in the HashSet, do nothing.

Example:

Input
["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"]
[[], [1], [2], [1], [3], [2], [2], [2], [2]]
Output
[null, null, null, true, false, null, true, null, false]

Explanation
MyHashSet myHashSet = new MyHashSet();
myHashSet.add(1);      // set = [1]
myHashSet.add(2);      // set = [1, 2]
myHashSet.contains(1); // return True
myHashSet.contains(3); // return False, (not found)
myHashSet.add(2);      // set = [1, 2]
myHashSet.contains(2); // return True
myHashSet.remove(2);   // set = [1]
myHashSet.contains(2); // return False, (already removed)

Approach: We will approach this problem same as we approached our previous problem of designing HashMap. We just will take array of LinkedList of int instead of LinkedList of Key, Value pair as here we just need to store an integer Rest of the things remains same.


Implementation in C#:

public class MyHashSet 

{

    public MyHashSet() 

    {

        this.size = 2069;

        this.buckets = new LinkedList<int>[2069];

    }

    

    public void Add(int key) 

    {

        int index = key % this.size;

        if (this.buckets[index] == null)

        {

            this.buckets[index] = new LinkedList<int>();

        }

        LinkedListNode<int> node = this.buckets[index].First;

        while(node != null)

        {

            if (node.Value == key)

            {

                return;

            }

            node = node.Next;

        }    

        this.buckets[index].AddFirst(key);

    }

    

    public void Remove(int key) 

    {

        int index = key % this.size;

        if (this.buckets[index] == null)

        {

            return;

        }

        LinkedListNode<int> node = this.buckets[index].First;

        while(node != null)

        {

            if (node.Value == key)

            {

                this.buckets[index].Remove(node);

                return;

            }

            node = node.Next;

        }

    }

    

    public bool Contains(int key) 

    {

        int index = key % this.size;

        if (this.buckets[index] == null)

        {

            return false;

        }        

        LinkedListNode<int> node = this.buckets[index].First;

        while(node != null)

        {

            if (node.Value == key)

            {

                return true;

            }

            node = node.Next;

        }

        return false;

    }

    

    LinkedList<int>[] buckets;

    int size;

}


Complexity: O(1) for all the operations.

[LeetCode] Linked List Random Node

Problem: Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen.

Implement the Solution class:

  • Solution(ListNode head) Initializes the object with the integer array nums.
  • int getRandom() Chooses a node randomly from the list and returns its value. All the nodes of the list should be equally likely to be choosen.

Example:

Input
["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"]
[[[1, 2, 3]], [], [], [], [], []]
Output
[null, 1, 3, 2, 2, 3]

Explanation
Solution solution = new Solution([1, 2, 3]);
solution.getRandom(); // return 1
solution.getRandom(); // return 3
solution.getRandom(); // return 2
solution.getRandom(); // return 2
solution.getRandom(); // return 3
// getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning.


Approach: We can take a private member of type List and just put all the values of the input Linked List into it. Now whenever we will get a call of getRandom(), we can generate a random index and return the value at that index in that list. That's all!

Note that here we are assuming the list size is not getting changed very frequently. If it is the case then we just can't rely on this approach and we need to use Reservoir Sampling.


Implementation in C#:

public class Solution 

{

    public Solution(ListNode head) 

    {

        this.listValues = new List<int>();

        ListNode itr = head;

        while (itr != null)

        {

            this.listValues.Add(itr.val);

            itr = itr.next;

        }

    }

    

    public int GetRandom() {

        int index = new Random().Next() % this.listValues.Count;

        return this.listValues[index];

    }

    

    private List<int> listValues;

}


Complexity: O(n) for constructor and O(1) for getRandom()

[LeetCode] Swapping Nodes in a Linked List

Problem: You are given the head of a linked list, and an integer k.

Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node from the end (the list is 1-indexed).

Example:

Input: head = [1,2,3,4,5], k = 2
Output: [1,4,3,2,5]
Input: head = [7,9,6,6,7,8,3,0,9,5], k = 5
Output: [7,9,6,6,8,7,3,0,9,5]
Input: head = [1], k = 1
Output: [1]
Input: head = [1,2], k = 1
Output: [2,1]
Input: head = [1,2,3], k = 2
Output: [1,2,3]

Constraints:

  • The number of nodes in the list is n.
  • 1 <= k <= n <= 105
  • 0 <= Node.val <= 100


Approach: It's a straight forward problem to solve. You can understand the approach by just looking at the implementation.


Implementation in C#:

    public ListNode SwapNodes(ListNode head, int k) 

    {

        if (head == null)

        {

            return head;

        }

        ListNode node = head;

        // Get the kth node from the start

        for (int i = 1; i < k; ++i)

        {

            node = node.next;

        }        

        ListNode swapNode1 = node;

        ListNode swapNode2 = head;

        // Get the kth node from the end

        while (node.next != null)

        {

            swapNode2 = swapNode2.next;

            node = node.next;

        }

        int temp = swapNode1.val;

        swapNode1.val = swapNode2.val;

        swapNode2.val = temp;

        return head;

    }


Complexity: O(n)

Wednesday, October 20, 2021

[LeetCode] Merge In Between Linked Lists

Problem: You are given two linked lists: list1 and list2 of sizes n and m respectively. Remove list1's nodes from the ath node to the bth node, and put list2 in their place.

The blue edges and nodes in the following figure indicate the result:

Build the result list and return its head.

Example:


Input: list1 = [0,1,2,3,4,5], a = 3, b = 4, list2 = [1000000,1000001,1000002]
Output: [0,1,2,1000000,1000001,1000002,5]
Explanation: We remove the nodes 3 and 4 and put the entire list2 in their place. The blue edges and nodes in the above figure indicate the result.

Input: list1 = [0,1,2,3,4,5,6], a = 2, b = 5, list2 = [1000000,1000001,1000002,1000003,1000004]
Output: [0,1,1000000,1000001,1000002,1000003,1000004,6]
Explanation: The blue edges and nodes in the above figure indicate the result.

Constraints:

  • 3 <= list1.length <= 104
  • 1 <= a <= b < list1.length - 1
  • 1 <= list2.length <= 104


Approach: The approach is straight forward. We will store the (a - 1)th node and (b + 1)th node of list1. Now we can put list2 to the next of (a - 1)th node of list1. We traverse the whole list2 and in the end we will put the stored (b + 1)th node of list1 to the next of end of list2 and that's all.


Implementation in C#: 

    public ListNode MergeInBetween(ListNode list1, int a, int b, ListNode list2) 

    {

        ListNode node = list1;

        ListNode mergeStartNode = null;

        for (int i = 0; i <= b; ++i)

        {

            if (i == a - 1)

            {

                mergeStartNode = node;

            }        

            node = node.next;

        }        

        ListNode mergeEndNode = node;

        mergeStartNode.next = list2;

        node = list2;

        while (node.next != null)

        {

            node = node.next;

        }

        node.next = mergeEndNode;

        return list1;

    }


Complexity: O(n)

Thursday, September 30, 2021

[LeetCode] Split Linked List in Parts

Problem: Given the head of a singly linked list and an integer k, split the linked list into k consecutive linked list parts.

The length of each part should be as equal as possible: no two parts should have a size differing by more than one. This may lead to some parts being null.

The parts should be in the order of occurrence in the input list, and parts occurring earlier should always have a size greater than or equal to parts occurring later.

Return an array of the k parts.

Example:

Input: head = [1,2,3], k = 5
Output: [[1],[2],[3],[],[]]
Explanation:
The first element output[0] has output[0].val = 1, output[0].next = null.
The last element output[4] is null, but its string representation as a ListNode is [].

Input: head = [1,2,3,4,5,6,7,8,9,10], k = 3
Output: [[1,2,3,4],[5,6,7],[8,9,10]]
Explanation:
The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts.

Constraints:

  • The number of nodes in the list is in the range [0, 1000].
  • 0 <= Node.val <= 1000
  • 1 <= k <= 50


Approach: It's a straight forward problem to solve. Please have a look at implementation to understand the approach.


Implementation in C#:

    public ListNode[] SplitListToParts(ListNode head, int k) 

    {

        ListNode[] result = new ListNode[k];

        int countOfNodes = this.Count(head);

        int groupSize = countOfNodes / k;

        int extraNodes = countOfNodes % k;

        ListNode node = head;

        for (int i = 0; i < k && node != null; ++i)

        {

            result[i] = node;

            for (int j = 1; j < groupSize + (extraNodes > 0 ? 1 : 0); ++j)

            {

                node = node.next;

            }

            ListNode nextNode = node.next;

            // break the link

            node.next = null;

            node = nextNode;

            --extraNodes;

        }        

        return result;

    }

    

    private int Count(ListNode head)

    {

        int count = 0;

        ListNode node = head;

        while (node != null)

        {

            ++count;

            node = node.next;

        }

        return count;

    }


Complexity: O(n)

Tuesday, July 27, 2021

[LeetCode] Swap Nodes in Pairs

Problem: Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the values in the list's nodes (i.e., only nodes themselves may be changed.)

Example:

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


Approach: Not much to explain as it is very much a straight forward problem to solve. You can understand the approach by just looking at the implementation.


Implementation in C#:

        public ListNode SwapPairs(ListNode head)

    {

if (head?.next == null)
{
return head;
}
ListNode node1 = head;
ListNode node2 = head.next;
head = head.next;
ListNode prev = null;
while(node1 != null && node2 != null)
{
ListNode next = node2.next;
node2.next = node1;
node1.next = next;
if (prev != null)
{
prev.next = node2;
}
prev = node1;
node1 = next;
node2 = node1?.next;
}
return head;
}

Complexity: O(n)