---
# System prepended metadata

title: DSA HW 2

---

# DSA HW 2
## problem 1
```python=
def G(A, p, q, r):
    
    if (p > q) or ((q + 1) > r):
        print("Use -inf to discard this term.")
        return -99997
        
    # grow left from q-1 until p to maximize 
    # grow right from q until r-1 to maximize
    # p <--- q || q+1 ---> r

    leftSum = A[q] 
    leftMax = leftSum
    i = q - 1
    while i >= p:
        leftSum += A[i]
        if leftSum > leftMax:
            leftMax = leftSum
        i -= 1
    
    rightSum = A[q + 1]  
    rightMax = rightSum
    i = q + 2
    while i <= r:
        rightSum += A[i]
        if rightSum > rightMax:
            rightMax = rightSum
        i += 1
        
    return leftMax + rightMax
```
```python=
def H(A, p, q, r, l):
    
    print("H", p, q, r, l)

    if (p > q) or ((q + 1) > r) or ((r + 1) > l):
        print("Use -inf to discard this term.")
        return -99998
    
    # grow left from q-1 until p to maximize 
    # grow right from q until r-1 to maximize
    # p <--- q || q+1 ~ r || r+1 ---> l
    
    leftSum = A[q] 
    leftMax = leftSum
    i = q - 1
    while i >= p:
        leftSum += A[i]
        if leftSum > leftMax:
            leftMax = leftSum
        i -= 1
    
    rightSum = A[r + 1]  
    rightMax = rightSum
    i = r + 2
    while i <= l:
        rightSum += A[i]
        if rightSum > rightMax:
            rightMax = rightSum
        i += 1
    
    return leftMax + rightMax + sum(A[q + 1 :r + 1])
```
```python=
def F(A, p, l):  
    print("F", p, l)

    if p > l:  # should never happen
        print("Use -inf to discard this term.")
        return -99999
    
    if p == l:
        return A[p]
    
    q = int(p + (l - p) * 1 / 3)
    r = int(p + (l - p) * 2 / 3) + 1  # This `+ 1` is crucial.
    # Otherwise, we will never generate A[0]+A[1] to compare with others
        
    s1 = F(A, p, q)
    s2 = F(A, q + 1, r)
    s3 = F(A, r + 1, l)
    s12 = G(A, p , q, r)
    s23 = G(A, q + 1, r, l)
    s123 = H(A, p, q, r, l)
        
    return max(s1, s2, s3, s12, s23, s123)
```
```python=
A = [2, 13, -3, -2, -2, 4, -6, -10, -14, -17]
print(F(A, 0, len(A) - 1))
```

### time complexity

$$
T(n) = 3 T( \frac{n}{3}) + \Theta(\frac{7}{3} n)
$$

By Master Method case 2, 

$$
T(n) = n\lg n
$$

## problem 2

For convenience, map `[10, 1, 6, 20, 16, 8, 33, 15]` to `[4, 1, 2, 7, 6, 3, 8, 5]`

### insertion sort

```python=
for j = 2 to len(A):
	key = A[j]
	// insert A[j] into A[1..j-1]
	i = j - 1
	while i > 0 and A[i] > key:
		A[i + 1] = A[i]
		i = i - 1
	A[i + 1] = key
```

```python=
[4 || 1    2    7    6    3    8    5]
[1    4 || 2    7    6    3    8    5]
[1    2    4 || 7    6    3    8    5]
[1    2    4    7 || 6    3    8    5]
[1    2    4    6    7 || 3    8    5]
[1    2    3    4    6    7 || 8    5]
[1    2    3    4    6    7    8 || 5]
[1    2    3    4    5    6    7    8]
```

### merge sort

`[4, 1, 2, 7, 6, 3, 8, 5]`
- divide
`[4, 1, 2, 7]` + `[6, 3, 8, 5]`
- divide
`[4, 1]` + `[2, 7]` + `[6, 3]` + `[ 8, 5]`
`[4]` + `[1]` + `[2]` + `[7]` + `[6]` + `[3]` + `[8]` + `[5]`
    - A single item is trivially sorted.
- merge
`[1, 4]` + `[2, 7]` + `[3, 6]` + `[5, 8]`
- merge
`[1, 2, 4, 7]` + `[3, 5, 6, 8]`
- merge
`[1, 2, 3, 4, 5, 6, 7, 8]`

## problem 3

```python=
def maxHeapify(A, i, n):
    leftChild = 2 * i + 1
    rightChild = 2 * i + 2
    largestSoFar = i
    if leftChild <= n:  # index may go out of range!
        if A[leftChild] > A[i]:
            largestSoFar = leftChild
    if rightChild <= n:  # index may go out of range!
        if A[rightChild] > A[largestSoFar]: 
            largestSoFar = rightChild
    if largestSoFar != i:
        A[i], A[largestSoFar] = A[largestSoFar], A[i]  # put the largest at i
        maxHeapify(A, largestSoFar, n)  # Go check the following subtree
def buildMaxHeap(A):
    for i in range(len(A) // 2 - 1, -1, -1):
        maxHeapify(A, i, len(A) - 1)
        
def heapSort(A):
    buildMaxHeap(A)
    for i in range(len(A)-1, 0, -1):
        A[0], A[i] = A[i], A[0]
        maxHeapify(A, 0, i - 1)
A = [1, 7, 9, 3, 100, 13, 12, 5, 14]
heapSort(A)

```

```python=
                                 Initiate
[30, 18, 26, 3, 37, 5, 16, 29]
                                 Build
[37, 30, 26, 29, 18, 5, 16, 3]
                                 Swap 0, 7
[3, 30, 26, 29, 18, 5, 16, 37]
                                 Heaptify 0 .. 6
[30, 29, 26, 3, 18, 5, 16, 37]
                                 Swap 0, 6
[16, 29, 26, 3, 18, 5, 30, 37]
                                 Heaptify 0 .. 5
[29, 18, 26, 3, 16, 5, 30, 37]
                                 Swap 0, 5
[5, 18, 26, 3, 16, 29, 30, 37]
                                 Heaptify 0 .. 4
[26, 18, 5, 3, 16, 29, 30, 37]
                                 Swap 0, 4
[16, 18, 5, 3, 26, 29, 30, 37]
                                 Heaptify 0 .. 3
[18, 16, 5, 3, 26, 29, 30, 37]
                                 Swap 0, 3
[3, 16, 5, 18, 26, 29, 30, 37]
                                 Heaptify 0 .. 2
[16, 3, 5, 18, 26, 29, 30, 37]
                                 Swap 0, 2
[5, 3, 16, 18, 26, 29, 30, 37]
                                 Heaptify 0 .. 1
[5, 3, 16, 18, 26, 29, 30, 37]
                                 Swap 0, 1
[3, 5, 16, 18, 26, 29, 30, 37]
                                 Heaptify 0 .. 0
[3, 5, 16, 18, 26, 29, 30, 37]
                                 Finish
[3, 5, 16, 18, 26, 29, 30, 37]
```

## problem 4 Hoare Partition
[1, 7, 9, 3, 100, 13, 12, 5, 14]
## problem 5
### 5.1

```python=
class ListNode:
    def __init__(self, data):
        self.data = data
        self.next = None
        
class LinkedList:
    def __init__(self):
        self.head = None
    def append(self, data):
        newNode = ListNode(data)
        newNode.next = self.head
        self.head = newNode
        
def mergeTwo(l1: ListNode, l2:ListNode) -> ListNode:
    dummyHead = ListNode(-1)  # dummy head
    tail = dummyHead
    while l1 and l2:
        if l1.data < l2.data:
            tail.next = l1
            tail = tail.next
            l1 = l1.next
        else:
            tail.next = l2
            tail = tail.next
            l2 = l2.next
    if l1:
        tail.next = l1
    else:
        tail.next = l2
    return dummyHead.next
```
```python=+
myLinkedList1 = LinkedList()
myLinkedList1.append(5)
myLinkedList1.append(2)
myLinkedList1.append(1)

myLinkedList2 = LinkedList()
myLinkedList2.append(6)
myLinkedList2.append(3)

l3 = mergeTwo(myLinkedList1.head, myLinkedList2.head)
```
### 5.2
```python=+
def mergeMany(listOfListNode):
    tempNode = None
    while listOfListNode:
        tempNode = mergeTwo(listOfListNode[-1], tempNode)
        listOfListNode.pop()
    return tempNode
```
```python=+
myLinkedList1 = LinkedList()
myLinkedList1.append(5)
myLinkedList1.append(2)
myLinkedList1.append(1)

myLinkedList2 = LinkedList()
myLinkedList2.append(6)
myLinkedList2.append(3)

myLinkedList3 = LinkedList()
myLinkedList3.append(8)
myLinkedList3.append(7)
myLinkedList3.append(4)

mergeResult = mergeMany(
        [myLinkedList1.head, myLinkedList2.head, myLinkedList3.head])
```
### 5.3 
#### my original 5.2
The time complexity of my 5.2 is 
$$n+2n+...+(k-1)n = \sum_{i=1}^{k-1} i n = \Theta(nk^2)$$

#### revision
Using Divide&Conquer

```python=
def mergeMany_revision(listOfListNode):
    while len(listOfListNode) > 1:
        n = len(listOfListNode) // 2
        for i in range(0, n):
            listOfListNode[2 * i] = mergeTwo(listOfListNode[2 * i], 
                                             listOfListNode[2 * i + 1])
            listOfListNode.pop(2 * i + 1)
    return listOfListNode[0]
```

#### time complex of the revised

$$
\frac{k}{2}n+\frac{k}{4}2n+...+1\lg(k)n=\Theta(kn\lg(k))
$$

#### revision 2 -- priority queue

```python=
while pq not empty:
    current.next = MAXIMUM(pq)
    current = current.next
    EXTRCT-MAX(pq)
    if current.next exist:
        INSERT(pq, current.next)
        current
```


### 5.4 
Already used divide and conquer.