---
# System prepended metadata

title: SE103 Week 1 Post Session Solutions

---

# SE103 Week 1 Post Session Solutions
[Add Two Numbers](#Add-two-numbers-MSF)
[Copy List With Random Pointer](#Copy-List-With-Random-Pointer)
[Intersection of Two Lists](#Intersection-of-Two-Lists)
[Merge K Sorted Lists](#Merge-K-Sorted-Lists)
[Merge Two Sorted Lists](#Merge-Two-Sorted-Lists)
[Remove Duplicates II](#Remove-Duplicates-II)
[Reorder List](#Reorder-List)


## Add two numbers (MSF)
**Java**
```java
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode previous = null;
        while (head != null) {
            ListNode next_head = head.next;
            head.next = previous;
            previous = head;
            head = next_head;
        }
        return previous;
    }
    public ListNode addTwoNumbersLSF(ListNode l1, ListNode l2) {
        ListNode resultList = new ListNode(0);
        // Create a variable for walking the resulting list
        ListNode cur = resultList;

        // Track the previous node so that we can remove the current node if it
        // turned out to be unnecessary.
        ListNode prev = null;
        while (l1 != null || l2 != null) {
            if (l1 != null) {
                cur.val += l1.val;
                l1 = l1.next;
            }
            if (l2 != null) {
                cur.val += l2.val;
                l2 = l2.next;
            }
            // Create the next node with a value of 1 iff there is a digit
            // carried from this iteration; otherwise it has a value of 0.
            cur.next = new ListNode(cur.val / 10);
            cur.val %= 10;
            prev = cur;
            cur = cur.next;
        }

        // If the most recently added node has value 0, then it is a leading
        // zero and can be truncated.
        if (cur.val == 0) {
            prev.next = null;
        }
        return resultList;
    }

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        l1 = reverseList(l1);
        l2 = reverseList(l2);
        ListNode result = addTwoNumbersLSF(l1, l2);
        return reverseList(result);
    }
}
```

**Python**
```Python
# Reverse both linked lists, then use the same algorithm as the variation of
# this problem with the least significant digit first. Finally, reverse the
# result to get the solution in most signficiant digit first form and return
# it. Since reversing a linked list is linear time and the original algorithm
# was linear time, the running time of this algorithm is also linear time.
class Solution(object):
    def reverseList(self, head):
        previous = None
        while head:
            next_head = head.next
            head.next = previous
            previous = head
            head = next_head
        return previous

    def addTwoNumbersLSF(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        resultList = ListNode(0)
        # Create a variable for walking the resulting list
        cur = resultList

        # Track the previous node so that we can remove the current node if it
        # turned out to be unnecessary.
        prev = None
        while l1 or l2:
            if l1:
                cur.val += l1.val
                l1 = l1.next
            if l2:
                cur.val += l2.val
                l2 = l2.next
            # Create the next node with a value of 1 iff there is a digit
            # carried from this iteration; otherwise it has a value of 0.
            cur.next = ListNode(cur.val / 10)
            cur.val %= 10
            prev = cur
            cur = cur.next

        # If the most recently added node has value 0, then it is a leading
        # zero and can be truncated.
        if cur.val == 0:
            prev.next = None
        return resultList

    def addTwoNumbers(self, l1, l2):
        l1 = self.reverseList(l1)
        l2 = self.reverseList(l2)
        result = self.addTwoNumbersLSF(l1, l2)
        return self.reverseList(result)
```

## Copy List With Random Pointer
**Python**
```python
# The primary challenge in this problem is finding the node in the copied list
# corresponding to a particular node in the original list (to assign the proper
# value for the random pointer).
#
# One solution, which uses O(n) storage, is to create a hashtable which maps
# each node in the original list to a node in the copy of the list. Then, one
# could copy the list in a single pass, and correct the random pointers in a
# second pass by looking up the corresponding node in the hashtable.
#
# Another solution, which takes O(n^2) time, is to do a linear search through
# the original list to find the position of the random node and iterate to the
# corresponding position in the copied list.
#
# The solution below arranges the copied nodes to initially live immediately
# after their corresponding original node. This makes it easy to find all the
# random pointers in constant time without extra storage. After the random
# pointers have been assigned, the list is peeled apart like a zipper; all the
# odd nodes return to the original list and all the even nodes form the copied
# list.
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: RandomListNode
        :rtype: RandomListNode
        """
        if not head:
            return None
        # Step 1: Duplicate each node and put it after the existing node.
        cur = head
        while cur:
            originalNext = cur.next
            cur.next = RandomListNode(cur.label)
            cur.next.next = originalNext
            cur = originalNext

        # Step 2: Copy all the random pointers
        cur = head
        while cur:
            copyCur = cur.next
            if cur.random:
                copyCur.random = cur.random.next
            cur = cur.next.next

        # Step 3: Peel the list apart, and return the new copy
        cur = head
        copyHead = head.next
        copyCur = copyHead

        # Remainder of the combined list
        combined = head.next.next
        isCopy = False

        while combined:
            if not isCopy:
                cur.next = combined
                cur = cur.next
            else:
                copyCur.next = combined
                copyCur = copyCur.next
            isCopy = not isCopy
            combined = combined.next

        cur.next = None
        return copyHead
```

**Java**
```java
public class Solution {
    public RandomListNode copyRandomList(RandomListNode head) {
        if (head == null)
            return null;
        // Step 1: Duplicate each node and put it after the existing node.
        RandomListNode cur = head;
        while (cur != null) {
            RandomListNode originalNext = cur.next;
            cur.next = new RandomListNode(cur.label);
            cur.next.next = originalNext;
            cur = originalNext;
        }

        // Step 2: Copy all the random pointers
        cur = head;
        while (cur != null) {
            RandomListNode copyCur = cur.next;
            if (cur.random != null)
                copyCur.random = cur.random.next;
            cur = cur.next.next;
        }

        // Step 3: Peel the list apart, and return the new copy
        cur = head;
        RandomListNode copyHead = head.next;
        RandomListNode copyCur = copyHead;

        // Remainder of the combined list
        RandomListNode combined = head.next.next;
        boolean isCopy = false;

        while (combined != null) {
            if (!isCopy) {
                cur.next = combined;
                cur = cur.next;
            }
            else {
                copyCur.next = combined;
                copyCur = copyCur.next;
            }
            isCopy = !isCopy;
            combined = combined.next;
        }

        cur.next = null;
        return copyHead;
    }
}
```

## Intersection of Two Lists
**Python**
```python
# If two lists intersect and have different lengths, then the difference must
# show up in the non-intersecting parts of the list. Therefore, iterate through
# the longer list until the two lists are the same length, and then iterate the
# lists together to find the intersecting node if it exists.
# Computing the length of a linked list is a linear time operation. Iterating
# over the two lists together is also a linear time operation. Therefore, the
# overall running time is O(n).

class Solution(object):
    def getLen(self, headA):
        s = 0
        while headA:
            headA = headA.next
            s+=1
        return s

    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        Alen = self.getLen(headA)
        Blen = self.getLen(headB)
        if Alen < Blen:
            delta = Blen - Alen
            # Advance B's head until the two lists are the same length.
            for _ in xrange(delta):
                headB = headB.next
        else:
            delta = Alen - Blen
            # Advance A's head until the two lists are the same length.
            for _ in xrange(delta):
                headA = headA.next
        while headA:
            if headA == headB:
                return headA
            headA = headA.next
            headB = headB.next
        return None
```

**Java**
```java
public class Solution {
    public int getLen(ListNode head) {
        int count = 0;
        while (head != null) {
            head = head.next;
            count++;
        }
        return count;
    }
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int Alen = getLen(headA);
        int Blen = getLen(headB);
        if (Alen < Blen) {
            int delta = Blen - Alen;
            // Advance B's head until the two lists are the same length.
            for (int i = 0; i < delta; i++)
                headB = headB.next;
        }
        else {
            int delta = Alen - Blen;
            // Advance A's head until the two lists are the same length.
            for (int i = 0; i < delta; i++)
                headA = headA.next;
        }
        while (headA != null) {
            if (headA == headB)
                return headA;
            headA = headA.next;
            headB = headB.next;
        }
        return null;
    }
}
```

## Merge K Sorted Lists
**Python**
```python
# In the standard algorithm for merging two sorted linked lists, each step of
# the algorithm compares the head nodes of each list, and removes the smaller
# head, adding it to the merged list.
# A naive solution to this problem uses a linear search to find the
# minimum of the k heads at every iteration, but the running time would be 
# O(kn), where k is the number of linked lists and n is the sum of the length
# of each linked list.
# The solution below holds the smallest node in each linked list in a heap, a
# data structure in which both extraction of the smallest element and insertion
# of any element is O(log k).  The heap is initialized with the smallest
# element from each list. At each iteration, the smallest element from the
# heap is added to the output linked list, and its child node (if not null) is
# added into the heap. The algorithm iterates until all the origial lists are
# exhausted. Since each iteration takes 2 operations which take O(log k) time
# and there are n iterations, the overall running time is O(n log k).
import heapq

# Python's heapq interface is a little verbose and doesn't support keys; this
# class is a thin wrapper that makes it slightly easier to deal with.
class Heap:
    def __init__(self, key=lambda x: x):
        self.storage = []
        self.key = key

    # Discard attempts to push None.
    def push(self, item):
        if item:
            heapq.heappush(self.storage, (self.key(item), item))

    def pop(self):
        try:
            _, item = heapq.heappop(self.storage)
            return item
        except:
            return None

    def length(self):
        return len(self.storage)


class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        # Initialize: Add the smallest node from each list into the heap.
        minElements = Heap(key=lambda x : x.val)

        for i in xrange(len(lists)):
            if lists[i]:
                minElements.push(lists[i])
                lists[i] = lists[i].next

        # Use the dummy head node to avoid handling the first element
        # differently than the remaining elements.
        dummyHead = ListNode("dummy")
        parent = dummyHead

        # Take the smallest element in the heap, and push its child
        # onto the heap.
        while minElements.length() != 0:
            cur = minElements.pop()
            minElements.push(cur.next)
            parent.next = cur
            parent = parent.next
        return dummyHead.next

```

**Java**
```java
class Solution {
    static class ListNodeComparator implements Comparator<ListNode> {
        public int compare(ListNode a, ListNode b) {
            return a.val - b.val;
        }
    }
    public ListNode mergeKLists(ListNode[] lists) {
        // Initialize: Add the smallest node from each list into the heap.
        // We use lists.length + 1 because Java PriorityQueue does not allow
        // initial capacity of 0.
        PriorityQueue<ListNode> minElements = new PriorityQueue(lists.length + 1, new ListNodeComparator());
        for (int i = 0; i < lists.length; i++) {
            if (lists[i] != null) {
                minElements.offer(lists[i]);
                lists[i] = lists[i].next;
            }
        }
        // Use the dummy head node to avoid handling the first element
        // differently than the remaining elements.
        ListNode dummyHead = new ListNode(-1);
        ListNode parent = dummyHead;

        // Take the smallest element in the heap, and push its child
        // onto the heap.
        while (minElements.size() != 0) {
            ListNode cur = minElements.poll();
            if (cur.next != null)
                minElements.offer(cur.next);
            parent.next = cur;
            parent = parent.next;
        }
        return dummyHead.next;
    }
}
```

## Merge Two Sorted Lists
**Python**
```python
# As long as both lists have elements, repeatedly compare their smallest
# elements and move the smaller element to the merged list. When one list is
# exhausted, append the remaining chunk of the remaining list to the merged
# list.
# This algorithm consumes one node from one of the lists at every iteration,
# and performs a constant amount of work to do so. Therefore, the running time
# is O(m + n), where m and n are the lengths of the lists.
class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        dummyHead = ListNode("dummy")
        cur = dummyHead
        while l1 and l2:
            if l1.val < l2.val:
                cur.next = l1
                l1 = l1.next
            else:
                cur.next = l2
                l2 = l2.next
            cur = cur.next
        if not l1:
            cur.next = l2
        else:
            cur.next = l1
        return dummyHead.next
```

**Java**
```java
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // 0 is an arbitrary value here
        ListNode dummyHead = new ListNode(0);
        ListNode cur = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                l1 = l1.next;
            } else {
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        if (l1 == null)
            cur.next = l2;
        else
            cur.next = l1;
        return dummyHead.next;
    }
}
```


## Remove Duplicates II
**Python**
```python
# The high level idea is to start at some non-duplicate parent node and
# examine its child to determine whether the child is a duplicate. If the child
# is a duplicate, then remove the child and all of its duplicates by pointing
# the parent's next pointer at the first node with a value that does not match
# the original child's value. If the child is not a duplicate, then the child
# becomes the new parent. In order to guarantee that the first parent is not a
# duplicate, we create a dummy head with a value not equal to the original head
# and use that as the initial parent.
# Since this algorithm either advances the parent pointer or eliminates at
# least one child in each iteration, and each iteration takes a constant amount
# of work, it completes in O(n) time.
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # Attach a dummy head so that duplicate entries at the start don't need
        # to be a special case.
        dummyHead = ListNode("dummy")
        dummyHead.next = head

        parent = dummyHead
        current = parent.next
        while parent and current:
            # No duplicate, advance both pointers.
            if not current.next or current.next.val != current.val:
                parent = current
                current = current.next
                continue

            # There is a duplicate, advance the second pointer until it points
            # to a node with a value different from its previous value.
            val = current.val
            while current and current.val == val:
                current = current.next
            parent.next = current

        return dummyHead.next
```

**Java**
```java
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        // Attach a dummy head so that duplicate entries at the start don't need
        // to be a special case.
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode parent = dummyHead;
        ListNode current = parent.next;
        while (parent != null && current != null) {
            // No duplicate, advance both pointers.
            if (current.next == null || current.next.val != current.val) {
                parent = current;
                current = current.next;
                continue;
            }
            // There is a duplicate, advance the second pointer until it points
            // to a node with a value different from its previous value.
            int val = current.val;
            while (current != null && current.val == val) {
                current = current.next;
            }
            parent.next = current;
        }
        return dummyHead.next;
    }
}
```

## Reorder List
**Python**
```python
# The key challenge in writing an efficient solution is figuring out how to
# iterate from both ends of the list at the same time.
# The key insight is to chop the list at the halfway point, and reverse the
# second half of the linked list. Then, one can iterate both lists
# simultaneously and zip them together to achieve the desired reordering.
# Since linked list reversal and traversal are both linear time, the overall
# algorithm is also O(n).

class Solution(object):
    def reverseList(self, head):
        previous = None
        while head:
            next_head = head.next
            head.next = previous
            previous = head
            head = next_head
        return previous

    def getLen(self, head):
        s = 0
        while head:
            head = head.next
            s+=1
        return s

    def reorderList(self, head):
        """
        :type head: ListNode
        :rtype: void Do not return anything, modify head in-place instead.
        """
        # Split the list in half and reverse the second half
        halfLength = (self.getLen(head) + 1) / 2
        secondHalf = head
        parent = None
        for _ in xrange(halfLength):
            parent = secondHalf
            secondHalf = secondHalf.next
        secondHalf = self.reverseList(secondHalf)

        # Truncate the first half of the list.
        if parent:
            parent.next = None

        # Zip the two pieces together
        while head and secondHalf:
            nextHead = head.next
            nextSecond = secondHalf.next
            head.next = secondHalf
            secondHalf.next = nextHead
            head = nextHead
            secondHalf = nextSecond
```

**Java**
```java
class Solution {
    private ListNode reverseList(ListNode head) {
        ListNode previous = null;
        while (head != null) {
            ListNode next_head = head.next;
            head.next = previous;
            previous = head;
            head = next_head;
        }
        return previous;
    }
    private int getLen(ListNode head) {
        int count = 0;
        while (head != null) {
            head = head.next;
            count++;
        }
        return count;
    }

    public void reorderList(ListNode head) {
        // Split the list in half and reverse the second half
        int halfLength = (getLen(head) + 1) / 2;
        ListNode secondHalf = head;
        ListNode parent = null;
        for (int i = 0; i < halfLength; i++) {
            parent = secondHalf;
            secondHalf = secondHalf.next;
        }
        secondHalf = reverseList(secondHalf);

        // Truncate the first half of the list.
        if (parent != null)
            parent.next = null;

        // Zip the two pieces together
        while (head != null && secondHalf != null) {
            ListNode nextHead = head.next;
            ListNode nextSecond = secondHalf.next;
            head.next = secondHalf;
            secondHalf.next = nextHead;
            head = nextHead;
            secondHalf = nextSecond;
        }
    }
}
```
