Pythonic Way to Get Unique Neighboring Integers in an Ordered List

Viewed 132

Given an ordered integer list, return the largest integer less than N and smallest integer greater than N. If there is none for one, just print "X".

5 Answers

Here ya go! Your code is especially slow with big number differences. e.g. [0, 752,15000,670000,37452846,3826848827,10000000000] with the number argument 40000000. I suppose you could also do a binary search type of thing if you wanted reeeallly huuuuge lists, but those are super annoying to make.

def solution(list_, n):
    l = list(filter(lambda p: p != n, sorted(list_[:])))
    for i, x in enumerate(l):
        if x > n: break
    else: return [l[-1], "X"]
    return [l[i-1], l[i]] if i else ["X", l[i]]

Hope you understanding this

li = [2, 4, 6, 8]
n = 9

#l for largest int lesser than n, s for smallest int greater than n
l = 'X'
s = 'X'

#insert n to list and ordered it
li.append(n)
li.sort()

#get the index of n in the list
index_n = li.index(n)

#if the n is not the smallest value in list, we assign the value before n to l
if index_n > 0:
    l = str(li[index_n - 1])

#if the n is not the greatest value in list, we assign the value after n to s
if index_n < len(li)-1:
    s = str(li[index_n + 1])
    
print(l,s)
    

No need to overthink it; this was surprisingly fast on my system (some modern laptop i7) and feels very Pythonic

# /usr/bin/env python3

# create ordered list of values
# NOTE that this need not be included in the algorithm time
l = list(range(0, 25000*3, 3))
print(len(l))

# pick some large n outside of the list l
n = 1000000

# try to find the min and max before and after N
try:
    a = max(x for x in l if x < n)
except ValueError:
    a = 'X'

try:
    b = min(x for x in l if x > n)
except ValueError:
    b = 'X'

print(a,b)
% time python3 so64964821.py
25000
74997 X
python3 so64964821.py  0.02s user 0.01s system 87% cpu 0.036 total

As far as I know, this is the fastest solution possible.

def binary_search(arr, x):
    low = 0
    high = len(arr) - 1
    mid = 0
    lm = -1
    while low <= high:
        lm = mid
        mid = (high + low) // 2
        if arr[mid] < x:
            low = mid + 1
        elif arr[mid] > x:
            high = mid - 1
        else:
            return mid
        if lm == mid:break
    return mid

def find_next_bigger(arr,n,i):
    while i < len(arr):
          if arr[i] > n:
              return arr[i]
          i += 1
    return arr[i] if i < len(arr) else "X"

def find_next_smaller(arr, n,i):
        while i >= 0:
            if arr[i] < n:
                return arr[i]
            i -= 1
        return arr[i] if i > 0 else "X"

def solution(arr, n):
    if n > arr[-1]:
        return arr[-1], "X"
    if arr[0] > n:
          return "X", arr[0]
    i = binary_search(arr, n)
    bigger = find_next_bigger(arr, n, i)
    smaller = find_next_smaller(arr,n,i)
    return smaller, bigger

Using the standard library keeps things simple-- and library methods are generally well-optimised and fast. The bisect module (link) has what you want:

from bisect import bisect_left
sorted_list = list(range(0, 50000, 2))
insertion_point = bisect_left(sorted_list, 15001)
below = sorted_list[insertion_point - 1]
above = sorted_list[insertion_point]
print(below, above)

$ python bis.py
15000 15002

bisect_left uses a binary search to find the "insertion point"-- the index where you'd insert this new number in the list to maintain the current sort order. That is exactly what you need to find your number's neighbors.

That's a start. You'll need a bit more logic to print 'X' if your number falls outside the range of integers in the sorted list.

Source code for bisect_left can be found here which is worth a look. Beautifully simple.

Related