---
# System prepended metadata

title: CSPT10 Hash Tables IV

---

# CSPT10 Hash Tables IV

## Fair Candy Swap
```
"""
https://leetcode.com/problems/fair-candy-swap/solution/

Understand

A = [1,1], B = [2,2]
[1,2]

A = [1], B = [2,3]
[1,3]

A = [1], B = [1]
[1,1]

Plan
For every candy x that Alice gives out, in order to have a fair swap, then she expects to get candy y in return.
Similar to Bob, if he gives out candy y, then he expects candy x in return. Since both of them need to have the same
amount of candies, we get the following equation:

sum(alice) - x + y = sum(bob) - y + x

If we solve for y, then we get the following:
2y = sum(bob) - sum(alice) + 2x
y = (sum(bob) - sum(alice) + 2x) / 2
y = (sum(bob) - sum(alice) / 2) + x

Therefore, if we try each candy x that Alice has, if Bob has candy y then we can do a fair swap. We use a set so that
we don't have to go through all of Bob's candies.

Runtime: O(alice num candies + bob num candies)
Space: O(bob num candies)
"""

class Solution:
    def fairCandySwap(self, A: List[int], B: List[int]) -> List[int]:
        bobCandies = set(B)
        bobSum, aliceSum = sum(B), sum(A)
        for x in A:
            y = ((bobSum - aliceSum) / 2) + x
            if y in bobCandies:
                return [x, y]
        return []

```
## Fibonacci
```
"""
https://leetcode.com/problems/fibonacci-number

Understand

Input: 2
Output: 1
Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1.

Input: 4
Output: 3
Explanation: F(4) = F(3) + F(2) = 2 + 1 = 3.

Input: 5
Output:
Explanation: F(5) = F(4) + F(3) = 3 + 2 = 5

Show them that things are being recomputed again

Plan
Use memoization to cache previously computed values to avoid recomputing them again

Runtime: O(N) Each number, starting at 2 up to and including N, is visited, computed and then stored for O(1)O(1) access later on.
Space: O(N) The dictionary will have at most N key-value pairs
"""

class Solution:
    def fib(self, N: int) -> int:
        computedValues = {1: 1, 0: 0}
        return self.memoizedFib(N, computedValues)

    def memoizedFib(self, N, computedValues):
        if N in computedValues:
            return computedValues[N]
        computedValues[N] = self.memoizedFib(N - 1, computedValues) + self.memoizedFib(N - 2, computedValues)
        return computedValues[N]

```
## Climbing Stairs
```
"""
https://leetcode.com/problems/climbing-stairs/

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

Plan

Use top-down approach and memoization to avoid pre-computing values.
To get the number of ways to reach the Nth step, add the number of ways
to reach the n - 1th step + n - 2nd step.

Runtime: O(n)
Space: O(n)
"""

class Solution:
    def climbStairs(self, n: int) -> int:
        computedValues = {0: 0, 1: 1, 2: 2}
        return self.memoizedClimbStairs(n, computedValues)

    def memoizedClimbStairs(self, n, computedValues):
        if n in computedValues:
            return computedValues[n]
        computedValues[n] = self.memoizedClimbStairs(n - 1, computedValues) + self.memoizedClimbStairs(n - 2, computedValues)
        return computedValues[n]

```
## Server Demo
```
import random

class Server:
    cache = {}

    def processGETRequest(self, url):
        if url in self.cache:
            print(url + '  is already cached')
            return self.cache[url]
        print(url + ' is not yet cached, computing...')
        self.cache[url] = self.doExpensiveComputation(url)
        print('caching ' + url + ' with value ' + str(self.cache[url]))
        return self.cache[url]

    def doExpensiveComputation(self, url):
        time.sleep(5)
        return random.randint(0, 101)

server = Server()
server.processGETRequest('hello')
server.processGETRequest('bar')
server.processGETRequest('hello')
server.processGETRequest('baz')
server.processGETRequest('hello')

```