The missing number problem

A list contains all the ints between 1 and n, but one of them is missing. (The list is of length n-1). What is the missing integer?

Here is one possible solution:

In [1]:
def missing_k(L):
    '''Assuming L contains all the number between 1 and n (exactly once), 
    except for k, return k
    
    Arguments:
    L -- a list of integers between 1 and n, with k missing
    
    '''
    n = len(L) + 1  #there are n numbers in total, one is missing, so len(L)=n-1
                    #so n = len(L) + 1
    
    #Try every k: 1, 2, 3, ..., n, and see which
    #k is missing
    for k in range(1, n+1):
        if k not in L:
            return k
    #will never get here, since one of the k's will 
    #not be in L and we'll return it

Let's rewrite this a little bit, imagining that we can't use k not in L.

In [2]:
def elem_in_list(elem, L):
    '''Return True iff elem is in L'''
    for e in L:
        if e == elem:
            return True
    
    #Tried every element, and didn't find any of them
    #in L
    return False

def missing_k_A(L):
    n = len(L) + 1
    for k in range(1, n+1):
        if not elem_in_list(k, L):
            return k

Note that we have to look through the list many times (n times, in fact -- we potentially look through the entire list every time we call elem_in_list()) in order to solve the problem this way.

Rewriting missing_k_A() to use a nested loop

For the sake of practice, let's rewrite missing_k_A() so that it doesn't use a loop. This is not as trivial as it might sound. Note that in elem_in_list(), we use a trick -- we return True when we find an element equal to elem, which means that we avoid ever reaching the statement return False. We have to find a substitute for that trick now.

In [3]:
def missing_k_B(L):
    n = len(L) + 1  
    
    for i in range(1, n+1):
        
        found = False #found an element equal to i
                      #in L
        
        for e in L:
            if e == i:
                found = True
                break #go to if not found
                
        if not found:
            return i        

Here, found is what's called a flag variable (or sentinel value). Its job is to become True if we want to continue the (outer) loop. When found stays False, we want to return.

Nested loops

Nested loops are loops within loops. An example is above. As you can see, we repeat the same thing for every i. We did that in missing_k_A() as well, except we called a function that has a loop inside it instead of explicitly having a loop inside the outer loop.

It's probably better, at least in this case, to have a helper function.

Another idea

Here's another idea -- and one that doesn't require us to go through the list so many times. Set up a list ind of length n+1, and set it all to False. Then, for every element e in L, set ind[e] to True (also set ind[0] to True). Then, we can figure out what the missing element is by finding where the only False is left. For example, for the list L = [5, 4, 1, 2], eventually the list ind will be

ind = [True, True, True, False, True, True], and since the False is at index 3, we know that 3 is the missing index.

In [4]:
def missing_k_C(L):
    n = len(L) + 1  
    ind = [False] * (n+1)
    ind[0] = True
    
    for e in L:
        ind[e] = True
        
    return ind.index(False)