|
| 1 | +Daily Coding Problem #4 |
| 2 | +Problem |
| 3 | +This problem was asked by Stripe. |
| 4 | + |
| 5 | +Given an array of integers, find the first missing positive integer in linear time and constant space. In other words, find the lowest positive integer that does not exist in the array. The array can contain duplicates and negative numbers as well. |
| 6 | + |
| 7 | +For example, the input [3, 4, -1, 1] should give 2. The input [1, 2, 0] should give 3. |
| 8 | + |
| 9 | +You can modify the input array in-place. |
| 10 | + |
| 11 | +Solution |
| 12 | +Our lives would be easier without the linear time constraint: we would just sort the array, while filtering out negative numbers, and iterate over the sorted array and return the first number that doesn't match the index. However, sorting takes O(n log n), so we can't use that here. |
| 13 | + |
| 14 | +Clearly we have to use some sort of trick here to get it running in linear time. Since the first missing positive number must be between 1 and len(array) + 1 (why?), we can ignore any negative numbers and numbers bigger than len(array). The basic idea is to use the indices of the array itself to reorder the elements to where they should be. We traverse the array and swap elements between 0 and the length of the array to their value's index. We stay at each index until we find that index's value and keep on swapping. |
| 15 | + |
| 16 | +By the end of this process, all the first positive numbers should be grouped in order at the beginning of the array. We don't care about the others. This only takes O(N) time, since we swap each element at most once. |
| 17 | + |
| 18 | +Then we can iterate through the array and return the index of the first number that doesn't match, just like before. |
| 19 | + |
| 20 | +def first_missing_positive(nums): |
| 21 | + if not nums: |
| 22 | + return 1 |
| 23 | + for i, num in enumerate(nums): |
| 24 | + while i + 1 != nums[i] and 0 < nums[i] <= len(nums): |
| 25 | + v = nums[i] |
| 26 | + nums[i], nums[v - 1] = nums[v - 1], nums[i] |
| 27 | + nums[v - 1] = v |
| 28 | + if nums[i] == nums[v - 1]: |
| 29 | + break |
| 30 | + for i, num in enumerate(nums, 1): |
| 31 | + if num != i: |
| 32 | + return i |
| 33 | + return len(nums) + 1 |
| 34 | +Another way we can do this is by adding all the numbers to a set, and then use a counter initialized to 1. Then continuously increment the counter and check whether the value is in the set. |
| 35 | + |
| 36 | +def first_missing_positive(nums): |
| 37 | + s = set(nums) |
| 38 | + i = 1 |
| 39 | + while i in s: |
| 40 | + i += 1 |
| 41 | + return i |
| 42 | +This is much simpler, but runs in O(N) time and space, whereas the previous algorithm uses no extra space. |
| 43 | + |
| 44 | + |
| 45 | +################################################################################################################################ |
| 46 | + |
| 47 | +Daily Coding Problem #5 |
| 48 | +Problem |
| 49 | +This problem was asked by Jane Street. |
| 50 | + |
| 51 | +cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last element of that pair. For example, car(cons(3, 4)) returns 3, and cdr(cons(3, 4)) returns 4. |
| 52 | + |
| 53 | +Given this implementation of cons: |
| 54 | + |
| 55 | +def cons(a, b): |
| 56 | + def pair(f): |
| 57 | + return f(a, b) |
| 58 | + return pair |
| 59 | +Implement car and cdr. |
| 60 | + |
| 61 | +Solution |
| 62 | +This is a really cool example of using closures to store data. We must look at the signature type of cons to retrieve its first and last elements. cons takes in a and b, and returns a new anonymous function, which itself takes in f, and calls f with a and b. So the input to car and cdr is that anonymous function, which is pair. To get a and b back, we must feed it yet another function, one that takes in two parameters and returns the first (if car) or last (if cdr) one. |
| 63 | + |
| 64 | +def car(pair): |
| 65 | + return pair(lambda a, b: a) |
| 66 | + |
| 67 | +def cdr(pair): |
| 68 | + return pair(lambda a, b: b) |
| 69 | +Fun fact: cdr is pronounced "cudder"! |
0 commit comments