检查列表是否已排序的Python方法


145

有没有一种pythonic的方法来检查列表是否已经排序ASCDESC

listtimestamps = [1, 2, 3, 5, 6, 7]

诸如此类的东西isttimestamps.isSorted()会返回TrueFalse

我想输入一些消息的时间戳列表,并检查事务是否以正确的顺序出现。

Answers:


212

实际上,我们没有给出anijhaw寻找的答案。这是一个班轮:

all(l[i] <= l[i+1] for i in xrange(len(l)-1))

对于Python 3:

all(l[i] <= l[i+1] for i in range(len(l)-1))

2
真好。您可能希望将其包装在一个函数中,以便可以传递一个key函数来使用。key=lambda x, y: x < y很好的默认值。
aaronasterling

3
几个解决方案的组合:def isSorted(x, key = lambda x: x): return all([key(x[i]) <= key(x[i + 1]) for i in xrange(len(x) - 1)])
eacousineau 2011年

2
@aaronasterling:operator.le应该比lambda快
玛丽安(Marian)

结果不适用于我(python --version = 2.6.4)l = [1, 2, 3, 4, 1, 6, 7, 8, 7] all(l[i] <= l[i+1] for i in xrange(len(l)-1)) 打印:True
prodev_paris 2015年

1
看起来Python 3.x xrange不再可用,只需使用即可range。我NameError: name 'xrange' is not defined在运行该代码时得到。我将其切换为仅使用range而不是,xrange并且效果很好。请参阅:stackoverflow.com/questions/15014310/...
卡尔·斯威尼

78

我只会用

if sorted(lst) == lst:
    # code here

除非这是一个很大的列表,否则您可能需要创建一个自定义函数。

如果您只是要对它进行排序(如果未排序的话),那么请忘记对它进行排序。

lst.sort()

并不要考虑太多。

如果您想使用自定义功能,可以执行以下操作

def is_sorted(lst, key=lambda x: x):
    for i, el in enumerate(lst[1:]):
        if key(el) < key(lst[i]): # i is the index of the previous element
            return False
    return True

如果列表已经被排序了,那么它将是O(n)(那时O(n)处于for循环中!)因此,除非您希望大多数时间都不对列表进行排序(并且相当随机),否则,再次,只需对列表进行排序。


10
如果这是您要执行的操作,则不妨说:lst.sort(),无需条件检查;-)
SapphireSun 2010年

5
多数民众赞成在正确的O(n)中使用简单的for循环,这显然是一种更快的方法。
anijhaw

1
@SapphireSun。我就是
这么

@anijhaw,请参阅我在发表评论时所做的更新。检查是O(n),排序是O(nlgn)。只是转过头来增加O(nlgn)还是只花对排序列表进行排序的成本(我相信)O(n)对timsort是更好的招致O(n)成本?
aaronasterling

@ Aaron:检查对原始问题的编辑,
anijhaw

44

该迭代器形式比使用整数索引快10-15%:

# python2 only
if str is bytes:
    from itertools import izip as zip

def is_sorted(l):
    return all(a <= b for a, b in zip(l, l[1:]))

我在我的机器上看不到明显的差异gist.github.com/735259 @Nathan Farrington的答案的修改后的#7变体比stackoverflow.com/questions/3755136/
jfs

这仅适用于“可索引”容器(如列表),在这种情况下,将使用切片创建两个新列表。对于一般的迭代器,我更喜欢Alexandre的解决方案
巴斯·温克尔斯

1
简洁的答案,您可以使用itertools izipislice从ittools使其更快。
Elmex80s

@jfs:“ Nathan Farrington的#7变体”是错误的。它只是没有做应该做的事情,这就是为什么它更快的原因。看到我的评论在那里。
olivecoder '18 -4-4

1
您可以简化对zip(l,l [1:])的解决方案,因为zip会在使用最短参数时停止运行
Gelineau

20

实现此目的的一种好方法是使用imap来自itertools以下函数:

from itertools import imap, tee
import operator

def is_sorted(iterable, compare=operator.le):
  a, b = tee(iterable)
  next(b, None)
  return all(imap(compare, a, b))

这种实现是快速的,并且适用于任何迭代。


4
不错,但是越野车!尝试使用is_sorted(iter([1,2,3,2,5,8]))或等效的发电机。您需要使用一个独立的迭代器进行tail尝试itertools.tee
2013年

记住,iter(x) is x对于迭代器
Kos 2013年

1
啊,这真是令人不快的惊喜!我已经修复了。谢谢!
亚历山大·瓦萨洛蒂

3
请注意,在Python 3 itertools.imap中已重命名为[__builtins__.]map
尼克T

10

我运行了一个基准测试sorted(lst, reverse=True) == lst对于长名单来说all(l[i] >= l[i+1] for i in xrange(len(l)-1))是最快的,对于短名单来说是最快的。这些基准测试是在MacBook Pro 2010 13英寸(Core2 Duo 2.66GHz,4GB 1067MHz DDR3 RAM,Mac OS X 10.6.5)上运行的。

更新:我修改了脚本,以便您可以在自己的系统上直接运行它。先前的版本存在错误。另外,我添加了已排序和未排序的输入。

  • 最适合短列表: all(l[i] >= l[i+1] for i in xrange(len(l)-1))
  • 最适合长排序列表: sorted(l, reverse=True) == l
  • 最适合简短的未排序列表: all(l[i] >= l[i+1] for i in xrange(len(l)-1))
  • 最适合长时间未排序的列表: all(l[i] >= l[i+1] for i in xrange(len(l)-1))

因此,在大多数情况下,都有明显的赢家。

更新: aaronsterling的答案(第6和第7名)实际上在所有情况下都是最快的。#7最快,因为它没有间接层来查找密钥。

#!/usr/bin/env python

import itertools
import time

def benchmark(f, *args):
    t1 = time.time()
    for i in xrange(1000000):
        f(*args)
    t2 = time.time()
    return t2-t1

L1 = range(4, 0, -1)
L2 = range(100, 0, -1)
L3 = range(0, 4)
L4 = range(0, 100)

# 1.
def isNonIncreasing(l, key=lambda x,y: x >= y): 
    return all(key(l[i],l[i+1]) for i in xrange(len(l)-1))
print benchmark(isNonIncreasing, L1) # 2.47253704071
print benchmark(isNonIncreasing, L2) # 34.5398209095
print benchmark(isNonIncreasing, L3) # 2.1916718483
print benchmark(isNonIncreasing, L4) # 2.19576501846

# 2.
def isNonIncreasing(l):
    return all(l[i] >= l[i+1] for i in xrange(len(l)-1))
print benchmark(isNonIncreasing, L1) # 1.86919999123
print benchmark(isNonIncreasing, L2) # 21.8603689671
print benchmark(isNonIncreasing, L3) # 1.95684289932
print benchmark(isNonIncreasing, L4) # 1.95272517204

# 3.
def isNonIncreasing(l, key=lambda x,y: x >= y): 
    return all(key(a,b) for (a,b) in itertools.izip(l[:-1],l[1:]))
print benchmark(isNonIncreasing, L1) # 2.65468883514
print benchmark(isNonIncreasing, L2) # 29.7504849434
print benchmark(isNonIncreasing, L3) # 2.78062295914
print benchmark(isNonIncreasing, L4) # 3.73436689377

# 4.
def isNonIncreasing(l):
    return all(a >= b for (a,b) in itertools.izip(l[:-1],l[1:]))
print benchmark(isNonIncreasing, L1) # 2.06947803497
print benchmark(isNonIncreasing, L2) # 15.6351969242
print benchmark(isNonIncreasing, L3) # 2.45671010017
print benchmark(isNonIncreasing, L4) # 3.48461818695

# 5.
def isNonIncreasing(l):
    return sorted(l, reverse=True) == l
print benchmark(isNonIncreasing, L1) # 2.01579380035
print benchmark(isNonIncreasing, L2) # 5.44593787193
print benchmark(isNonIncreasing, L3) # 2.01813793182
print benchmark(isNonIncreasing, L4) # 4.97615599632

# 6.
def isNonIncreasing(l, key=lambda x, y: x >= y): 
    for i, el in enumerate(l[1:]):
        if key(el, l[i-1]):
            return False
    return True
print benchmark(isNonIncreasing, L1) # 1.06842684746
print benchmark(isNonIncreasing, L2) # 1.67291283607
print benchmark(isNonIncreasing, L3) # 1.39491200447
print benchmark(isNonIncreasing, L4) # 1.80557894707

# 7.
def isNonIncreasing(l):
    for i, el in enumerate(l[1:]):
        if el >= l[i-1]:
            return False
    return True
print benchmark(isNonIncreasing, L1) # 0.883186101913
print benchmark(isNonIncreasing, L2) # 1.42852401733
print benchmark(isNonIncreasing, L3) # 1.09229516983
print benchmark(isNonIncreasing, L4) # 1.59502696991

1
基准测试是针对生成器表达式形式的最坏情况,对于我的解决方案是最佳情况。您可能还想针对未排序的列表进行测试。然后,您将看到,除非您期望大多数时间对列表进行排序,否则生成器表达式会更好。
aaronasterling 2010年

@aaronsterling,我已将脚本更新为具有已排序和未排序的输入。
内森·法灵顿2010年

的所有功能enumerate均不正确。enumerate(l[1:])应该由enumerate(l[1:], 1)
jfs

1
而不是替换enumerate(l[1:])enumerate(l[1:], 1),你可以取代l[i-1]l[i]
jfs

例如,如果添加随机输入,L5=range(100); random.shuffle(L5)则#5相对较慢。在这种情况下,修改后的#7整体速度更快codepad.org/xmWPxHQY
jfs

9

我会这样做的(从这里的很多答案中窃取了[亚伦·斯特林,伟业同志,保罗·麦奎尔(Paul McGuire)等等),大部分都是阿明·罗纳彻(Armin Ronacher):

from itertools import tee, izip

def pairwise(iterable):
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

def is_sorted(iterable, key=lambda a, b: a <= b):
    return all(key(a, b) for a, b in pairwise(iterable))

一件好事:您不必实现该系列的第二个可迭代对象(与列表切片不同)。


2
具有误导性的名称keykey应该用于将项目转换为可比较的值。
InQβ

4

我使用基于numpy.diff()的这种单线:

def issorted(x):
    """Check if x is sorted"""
    return (numpy.diff(x) >= 0).all() # is diff between all consecutive entries >= 0?

我还没有真正针对任何其他方法计时,但是我认为它比任何纯Python方法都快,尤其是对于大n而言,因为numpy.diff中的循环(可能)直接在C中运行(n-1个减法,后跟n个) -1比较)。

但是,如果x是无符号的int,则需要小心,这可能会导致numpy.diff()中的无声整数下溢,从而导致误报。这是修改后的版本:

def issorted(x):
    """Check if x is sorted"""
    try:
        if x.dtype.kind == 'u':
            # x is unsigned int array, risk of int underflow in np.diff
            x = numpy.int64(x)
    except AttributeError:
        pass # no dtype, not an array
    return (numpy.diff(x) >= 0).all()

4

这类似于最佳答案,但我更喜欢它,因为它避免了显式索引。假设列表名称为lst,则可以
(item, next_item)使用zip以下命令从列表中生成元组:

all(x <= y for x,y in zip(lst, lst[1:]))

在Python 3中,zip已经返回了生成器;在Python 2中,可以使用它itertools.izip来提高内存效率。

小型演示:

>>> lst = [1, 2, 3, 4]
>>> zip(lst, lst[1:])
[(1, 2), (2, 3), (3, 4)]
>>> all(x <= y for x,y in zip(lst, lst[1:]))
True
>>> 
>>> lst = [1, 2, 3, 2]
>>> zip(lst, lst[1:])
[(1, 2), (2, 3), (3, 2)]
>>> all(x <= y for x,y in zip(lst, lst[1:]))
False

(3, 2)评估元组时,最后一个失败。

奖励:检查无法索引的有限(!)生成器:

>>> def gen1():
...     yield 1
...     yield 2
...     yield 3
...     yield 4
...     
>>> def gen2():
...     yield 1
...     yield 2
...     yield 4
...     yield 3
... 
>>> g1_1 = gen1()
>>> g1_2 = gen1()
>>> next(g1_2)
1
>>> all(x <= y for x,y in zip(g1_1, g1_2))
True
>>>
>>> g2_1 = gen2()
>>> g2_2 = gen2()
>>> next(g2_2)
1
>>> all(x <= y for x,y in zip(g2_1, g2_2))
False

itertools.izip如果您使用的是Python 2,请确保在此处使用,否则您将失去不必从生成器创建列表的目的。


2
您甚至可以使用islice来优化切片。同样在itertools模块中。all(x <= y for x, y in izip(lst, islice(lst, 1)))
Elmex80s

3

SapphireSun是完全正确的。您可以使用lst.sort()。Python的排序实现(TimSort)检查列表是否已排序。如果这样,sort()将在线性时间内完成。听起来像是一种Python方式,可以确保对列表进行排序;)


20
实际上,如果对列表进行排序,则仅是线性时间。如果不是这样,就没有短路可以跳过实际的排序任务,因此如果列表很长,则可能要付出巨大的代价。
PaulMcG 2010年

如果您的任务是“确保列表已排序,否则列表将死亡”,这是一个很好的答案。作为出于其他原因应该排序的数据的健全性检查,这非常普遍。那么只有错误情况是缓慢的。
Ed Avis

3

尽管我认为不能保证该sorted内置函数使用调用其cmp函数i+1, i,但对于CPython来说确实如此。

因此,您可以执行以下操作:

def my_cmp(x, y):
   cmpval = cmp(x, y)
   if cmpval < 0:
      raise ValueError
   return cmpval

def is_sorted(lst):
   try:
      sorted(lst, cmp=my_cmp)
      return True
   except ValueError:
      return False

print is_sorted([1,2,3,5,6,7])
print is_sorted([1,2,5,3,6,7])

还是这样(如果if语句-> EAFP出了错?;-)):

def my_cmp(x, y):
   assert(x >= y)
   return -1

def is_sorted(lst):
   try:
      sorted(lst, cmp=my_cmp)
      return True
   except AssertionError:
      return False

3

完全不是Pythonic,但我们至少需要一个reduce()答案,对吗?

def is_sorted(iterable):
    prev_or_inf = lambda prev, i: i if prev <= i else float('inf')
    return reduce(prev_or_inf, iterable, float('-inf')) < float('inf')

累加器变量仅存储该最后检查的值,如果任何值小于前一个值,则累加器将设置为无穷大(因此,最后的值仍然为无穷大,因为“先前的值”将始终大于当前的)。


2

正如@aaronsterling所指出的那样,以下解决方案是最短的,并且在对数组进行排序且不太小时似乎是最快的:def is_sorted(lst):return(sorted(lst)== lst)

如果大多数情况下未对数组进行排序,则希望使用不扫描整个数组并在发现未排序前缀后立即返回False的解决方案。以下是我能找到的最快的解决方案,它并不是特别优雅:

def is_sorted(lst):
    it = iter(lst)
    try:
        prev = it.next()
    except StopIteration:
        return True
    for x in it:
        if prev > x:
            return False
        prev = x
    return True

使用Nathan Farrington的基准测试,在所有情况下都比使用sorted(lst)更好的运行时间,但在大型排序列表上运行时除外。

这是我计算机上的基准测试结果。

sorted(lst)== lst解决方案

  • L1:1.23838591576
  • L2:4.19063091278
  • L3:1.17992287346
  • L4:4.668399500847

第二种解决方案:

  • L1:0.81095790863
  • L2:0.802397012711
  • L3:1.06135106087
  • L4:8.82761001587

2

如果您想要最快的方法用于numpy数组,请使用numba,如果使用conda,则应已安装

该代码将很快,因为它将由numba编译

import numba
@numba.jit
def issorted(vec, ascending=True):
    if len(vec) < 2:
        return True
    if ascending:
        for i in range(1, len(vec)):
            if vec[i-1] > vec[i]:
                return False
        return True
    else:
        for i in range(1, len(vec)):
            if vec[i-1] < vec[i]:
                return False
        return True

然后:

>>> issorted(array([4,9,100]))
>>> True

2

只是添加另一种方式(即使它需要一个附加模块)iteration_utilities.all_monotone

>>> from iteration_utilities import all_monotone
>>> listtimestamps = [1, 2, 3, 5, 6, 7]
>>> all_monotone(listtimestamps)
True

>>> all_monotone([1,2,1])
False

要检查DESC订单:

>>> all_monotone(listtimestamps, decreasing=True)
False

>>> all_monotone([3,2,1], decreasing=True)
True

strict如果需要严格检查(如果连续元素不应相等)单调序列,则还有一个参数。

在您的情况下这不是问题,但是如果您的序列包含nan值,那么某些方法将失败,例如sorted:

def is_sorted_using_sorted(iterable):
    return sorted(iterable) == iterable

>>> is_sorted_using_sorted([3, float('nan'), 1])  # definetly False, right?
True

>>> all_monotone([3, float('nan'), 1])
False

请注意,iteration_utilities.all_monotone与此处提到的其他解决方案相比,该方法的执行速度更快,尤其是对于未排序的输入(请参阅基准)。


2

from itertools import tee

def is_sorted(l):
    l1, l2 = tee(l)
    next(l2, None)
    return all(a <= b for a, b in zip(l1, l2))

1
绝对棒!这是我的改进,使其成为单层-而不是iter()和next()使用切片具有相同的结果:all(a <= b for a, b in zip(l, l[1:]))
马特

1
@LiborJelinek很好,但是我的版本在当l是生成器并且不支持切片时可以工作。
Sergey11g

2

的Python 3.6.8

from more_itertools import pairwise

class AssertionHelper:
    @classmethod
    def is_ascending(cls, data: iter) -> bool:
        for a, b in pairwise(data):
            if a > b:
                return False
        return True

    @classmethod
    def is_descending(cls, data: iter) -> bool:
        for a, b in pairwise(data):
            if a < b:
                return False
        return True

    @classmethod
    def is_sorted(cls, data: iter) -> bool:
        return cls.is_ascending(data) or cls.is_descending(data)
>>> AssertionHelper.is_descending((1, 2, 3, 4))
False
>>> AssertionHelper.is_ascending((1, 2, 3, 4))
True
>>> AssertionHelper.is_sorted((1, 2, 3, 4))
True

0

最简单的方法:

def isSorted(arr):
  i = 1
  while i < len(arr):
    if(result[i] < result[i - 1]):
      return False
    i += 1
  return True

0
from functools import reduce

# myiterable can be of any iterable type (including list)
isSorted = reduce(lambda r, e: (r[0] and (r[1] or r[2] <= e), False, e), myiterable, (True, True, None))[0]

派生的减少值是(sortedSoFarFlagfirstTimeFlaglastElementValue)的三部分元组。它最初开始与(TrueTrueNone),其也被用作结果为空列表(被视为排序,因为有外的顺序没有元素)。在处理每个元素时,它会计算元组的新值(使用先前的元组值和下一个elementValue):

[0] (sortedSoFarFlag) evaluates true if: prev_0 is true and (prev_1 is true or prev_2 <= elementValue)
[1] (firstTimeFlag): False
[2] (lastElementValue): elementValue

减少的最终结果是一个元组:

[0]: True/False depending on whether the entire list was in sorted order
[1]: True/False depending on whether the list was empty
[2]: the last element value

第一个值是我们感兴趣的值,因此我们通常[0]从reduce结果中获取该值。


请注意,此解决方案适用于可以相互比较的任何可迭代包含元素类型。这包括的琴弦组布尔的列表(检查真值之前出现假值),号码清单,清单(按字母顺序),列表(超集之前发生的子集)等
先生黄鼠狼

0

由于我没有在上方看到此选项,因此将其添加到所有答案中。用表示列表l,然后:

import numpy as np

# Trasform the list to a numpy array
x = np.array(l)

# check if ascendent sorted:
all(x[:-1] <= x[1:])

# check if descendent sorted:
all(x[:-1] >= x[1:])

0

使用赋值表达式的解决方案(在Python 3.8中添加):

def is_sorted(seq):
    seq_iter = iter(seq)
    cur = next(seq_iter, None)
    return all((prev := cur) <= (cur := nxt) for nxt in seq_iter)

z = list(range(10))
print(z)
print(is_sorted(z))

import random
random.shuffle(z)
print(z)
print(is_sorted(z))

z = []
print(z)
print(is_sorted(z))

给出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
True
[1, 7, 5, 9, 4, 0, 8, 3, 2, 6]
False
[]
True

-1

实际上,这是使用递归实现的最短方法:

如果已排序将打印True,否则将打印False

 def is_Sorted(lst):
    if len(lst) == 1:
       return True
    return lst[0] <= lst[1] and is_Sorted(lst[1:])

 any_list = [1,2,3,4]
 print is_Sorted(any_list)

请注意,这将增加RuntimeError: maximum recursion depth exceeded长列表。尝试any_list = range(1000)
timgeb '16

-1

这个怎么样 ?简单明了。

def is_list_sorted(al):

    llength =len(al)


    for i in range (llength):
        if (al[i-1] > al[i]):
            print(al[i])
            print(al[i+1])
            print('Not sorted')
            return -1

    else :
        print('sorted')
        return  true

-3

绝对在Python 3及更高版本中适用于整数或字符串:

def tail(t):
    return t[:]

letters = ['a', 'b', 'c', 'd', 'e']
rest = tail(letters)
rest.sort()
if letters == rest:
    print ('Given list is SORTED.')
else:
    print ('List NOT Sorted.')

================================================== ===================

查找给定列表是否已排序的另一种方法

trees1 = list ([1, 4, 5, 3, 2])
trees2 = list (trees1)
trees2.sort()
if trees1 == trees2:
    print ('trees1 is SORTED')
else:
    print ('Not sorted')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.