# 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.