Problem:
Similarly to the problem A described in column 2: given a range [lower_bound, upper_bound], and a sorted list l; elements in l are within given range; some numbers within given range are missing in the list; find out one.
Solution #1:
Similarly to binary search, the key is to divide the list and refine the range in cycle. On each loop cycle:
1. Check end condition #1: the first element > lower_bound, or last element < upper_bound.
2. Divide the 'list' into two sub lists (find pivot index in the middle, and split the list to [0, pivot] and [pivot + 1, last].
3. Check end condition #2: compare l[pivot] and l[pivot + 1] and check if difference is 1.
4. Compare the length of each sublist, to the value difference (sublist[len(sublist)-1] - sublist[0]). If length is less, then update the loop variables: list <= sublist, range [sublist[0], sublit[last]].
Code for solution #1:
def prog_2_A_find_missing(l, lower_bound, upper_bound): """ >>> print(prog_2_A_find_missing([1, 2, 3, 5, 7, 8], 1, 8)) 4 >>> print(prog_2_A_find_missing([1, 2, 3, 4, 6, 7], 1, 7)) 5 """ lower = 0 upper = len(l) - 1 missing = -1 # on loop condition: # '=' means the list to be scanned can only contain one element # loop assertions: # - l[lower : upper] (including l[upper])indicates sorted list that contains missing numbers. # - range(lower_bound, upper_bound) indicates the range that missing numbers are within # - on each cycle, problem can be defined as: # sorted list l[lower : upper] contains numbers within range(lower_bound, upper_bound), # try to find the missing number (a number that is within range but not present in the list) while lower <= upper: # end conditions: found that given boundaries exceed list boundaries if l[lower] > lower_bound: missing = lower_bound break if l[upper] < upper_bound: missing = upper_bound break # deal with case where pivot_right > upper # this could happen when there is only one element in the list pivot_left = lower + ((upper - lower) >> 1) pivot_right = pivot_left + 1 if pivot_right > upper: break # another end condition: # the missing one is between pivot_left and pivot_right if l[pivot_right] - l[pivot_left] > 1: missing = l[pivot_right] - 1 break gap_left = l[pivot_left] - l[lower] + 1 gap_right = l[upper] - l[pivot_right] + 1 len_left = pivot_left - lower + 1 len_right = upper - pivot_right + 1 # compare length to find out which section contains missing element if gap_left > len_left: # find missing in left section upper = pivot_left upper_bound = l[upper] # does not really matter whether we have following statement # lower_bound = l[lower] elif gap_right > len_right: # find missing in right section lower = pivot_right lower_bound = l[lower] # does not really matter whether we have following statement # upper_bound = l[upper] # if they all equal, no missing element else: break return missing
Solution #2:
The solution is similar, except it does not require the list is sorted.
On each cycle, the list will be divided to two groups, one containing even numbers and the other containing odd numbers.
def prog_2_A_find_missing_bitwise(l, lower_bound, upper_bound): """ >>> print(prog_2_A_find_missing_bitwise([1, 2, 3, 5, 7, 8], 1, 8)) 6 >>> print(prog_2_A_find_missing_bitwise([1, 2, 3, 4, 6, 7], 1, 7)) 5 """ def split(numbers): set_odd = [num >> 1 for num in numbers if num & 1 == 1] set_even = [num >> 1 for num in numbers if num & 1 == 0] return (set_odd, set_even) # TODO: odd count of numbers # corner case: when numbers set has only one element, it will still work # cause either set_odd or set_even will be empty and that one will be the missing one numbers = l numbers_range = range(lower_bound, upper_bound + 1) # count = (upper_bound - lower_bound + 1) >> 1 bit_index = 0 num_missing = 0 # loop assertions: # - the list numbers contains missing numbers # - bit_index and num_missing records the path to calculate missing number while len(numbers) > 0: set_odd, set_even = split(numbers) range_odd, range_even = split(numbers_range) if len(set_odd) < len(range_odd): numbers = set_odd numbers_range = range_odd num_missing += (1 << bit_index) elif len(set_even) < len(range_even): numbers = set_even numbers_range = range_even num_missing += (0 << bit_index) # can be ignored, though... #count >>= 1 bit_index += 1 return num_missing