简体   繁体   中英

Check if list is sorted in either ascending or descending order in one pass

Is there a way to check if a list is sorted either in ascending or descending order in one pass?

From this answer , I modified to also support descending order check, so my code now looks like this, is there a way to check this without having to use two for loops?

def is_sorted(check):
    current = check
    forwarded = check[1:]
    ascending_check = all(i<=j for i, j in zip(current, forwarded))
    descending_check = all(i>=j for i, j in zip(current, forwarded))
    return ascending_check or descending_check

numbers = list(range(1, 10))
print(is_sorted(numbers))
reverse_numbers = list(range(10, 0, -1))
print(is_sorted(reverse_numbers))
not_sorted = [1, 3, 2, 4, 5]
print(is_sorted(not_sorted))

You can examine the first two elements, then return True if the entire list has the same ordering as those two. You have to also take into account the possibility that there is a tie at the beginning, and that the entire list only contains the same values throughout.

def is_sorted(check):
    ascending = True
    descending = True
    previous = check[0]
    for item in check[1:]:
        if ascending and item < previous:
            ascending = False
        if descending and item > previous:
            descending = False
        if not ascending and not descending:
            return False
        previous = item
    return True

Demo: https://ideone.com/YjBB9T

You could check pairs of consecutive elements, keep track of whether the first pair that is not == is < or > , and check that all the other pairs have the same relation (if not equal):

def is_sorted(lst):
    asc = None
    for i in range(len(lst)-1):
        a, b = lst[i], lst[i+1]
        if a == b:
            continue
        if asc is None:
            asc = (a < b)
        if (a < b) != asc:
            return False
    return True

That's a bit verbose, but you could also make it shorter with next and all , using a shared zip iterator for both so the entire list is iterated only once.

def is_sorted(lst):
    pairs = zip(lst, lst[1:])
    asc = next((a < b for a, b in pairs if a != b), None)
    return asc is None or all((a < b) == asc for a, b in pairs if a != b)

However, this version (like your original) will create a copy of the input list with lst[1:] , so that's not really just a single loop, either. To fix that, you could eg use itertools.tee or islice .

This would work:

def is_sorted(check):
    asc = desc = True
    i, L = 0, len(check)
    while (i < (L-1)) and (asc or desc):
        if check[i] < check[i+1]:
            desc = False
        elif check[i] > check[i+1]:
            asc = False
        if not (asc or desc):
            break
        i += 1
    if asc and desc:
        return "Ascending and Descending"
    elif asc:
        return "Ascending"
    elif desc:
        return "Descending"
    else:
        return "Not ascending and not descending"

Tests:

print (is_sorted([1, 2, 3]))
print (is_sorted([3, 2, 1]))
print (is_sorted([1, 2, 3, 2, 1]))
print (is_sorted([1, 1, 1]))

Output:

Ascending
Descending
Not ascending and not descending
Ascending and Descending

You can try to check if sorted ascending way, if not: array[::-1] , and check again. array[::-1] reverses the array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM