对列表进行排序:数字以升序排列,字母以降序排列

时间:2017-08-07 07:04:48

标签: python list sorting list-comprehension

此问题实际上是根据one previously asked by Mat.Simage)改编的。虽然它已被删除,但我认为这是一个很好的问题,所以我将它与更清晰的要求和我自己的解决方案重新发布。

给出一个字母和数字列表,比如说

['a', 2, 'b', 1, 'c', 3]

要求是按升序对数字进行排序,以降序对字母进行排序,而不改变字母和数字的相对位置。我的意思是,如果未排序的列表是:

[L, D, L, L, D]    # L -> letter; # D -> digit 

然后,排序列表也必须是

[L, D, L, L, D] 
  1. 的字母和数字必须以规则模式交替显示 - 它们可以按任意顺序出现

  2. 排序后 - 数字递增,字母下降。

  3. 因此,对于上面的示例,输出为

    ['c', 1, 'b', 2, 'a', 3]
    

    另一个例子:

     In[]: [5, 'a', 'x', 3, 6, 'b']
    Out[]: [3, 'x', 'b', 5, 6, 'a']
    

    这样做的好方法是什么?

6 个答案:

答案 0 :(得分:6)

以下是使用defaultdict()bisect()的优化方法:

In [14]: lst = [5, 'a', 'x', 3, 6, 'b']
In [15]: from collections import defaultdict       
In [16]: import bisect

In [17]: def use_dict_with_bisect(lst):
             d = defaultdict(list)
             for i in lst:
                 bisect.insort(d[type(i)], i)
             # since bisect doesn't accept key we need to reverse the sorted integers
             d[int].sort(reverse=True)
             return [d[type(i)].pop() for i in lst]
   .....:  

演示:

In [18]: lst
Out[18]: [5, 'a', 'x', 3, 6, 'b']

In [19]: use_dict_with_bisect(lst)
Out[19]: [3, 'x', 'b', 5, 6, 'a']

如果您正在处理较大的列表,那么使用bisect进行优化以使其具有关于O(n 2 )的复杂性并且只使用python具有Nlog(n)复杂性的内置sort()函数。

In [26]: def use_dict(lst):
             d = defaultdict(list)
             for i in lst:
                 d[type(i)].append(i)
             d[int].sort(reverse=True); d[str].sort()
             return [d[type(i)].pop() for i in lst]

其他答案的基准测试显示使用dict和内置sort的最新方法比其他方法快1ms

In [29]: def use_sorted1(lst):
              letters = sorted(let for let in lst if isinstance(let,str))
              numbers = sorted((num for num in lst if not isinstance(num,str)), reverse = True)
              return [letters.pop() if isinstance(elt,str) else numbers.pop() for elt in lst]
   .....: 

In [31]: def use_sorted2(lst):
              f1 = iter(sorted(filter(lambda x: isinstance(x, str), lst), reverse=True))
              f2 = iter(sorted(filter(lambda x: not isinstance(x, str), lst)))
              return [next(f1) if isinstance(x, str) else next(f2) for x in lst]
   .....: 

In [32]: %timeit use_sorted1(lst * 1000)
100 loops, best of 3: 3.05 ms per loop

In [33]: %timeit use_sorted2(lst * 1000)
100 loops, best of 3: 3.63 ms per loop

In [34]: %timeit use_dict(lst * 1000)   # <-- WINNER
100 loops, best of 3: 2.15 ms per loop

这是一个基准测试,显示使用bisect如何减慢长列表的进程:

In [37]: %timeit use_dict_with_bisect(lst * 1000)
100 loops, best of 3: 4.46 ms per loop

答案 1 :(得分:5)

看看,没有iter

lst = ['a', 2, 'b', 1, 'c', 3]
letters = sorted(let for let in lst if isinstance(let,str))
numbers = sorted((num for num in lst if not isinstance(num,str)), reverse = True)
lst = [(letters if isinstance(elt,str) else numbers).pop()for elt in lst]

我正在寻找一种方法将其变成(可怕的)单行,但到目前为止没有运气 - 建议欢迎!

答案 2 :(得分:4)

我通过创建两个生成器然后有条件地从它们中取出来解决了这个问题:

In [116]: f1 = iter(sorted(filter(lambda x: isinstance(x, str), lst), reverse=True))

In [117]: f2 = iter(sorted(filter(lambda x: not isinstance(x, str), lst)))

In [118]: [next(f1) if isinstance(x, str) else next(f2) for x in lst]
Out[118]: ['c', 1, 'b', 2, 'a', 3]

答案 3 :(得分:1)

在一行中:

list(map(list, sorted(zip(lst[::2], lst[1::2]), key=lambda x: x[1] if hasattr(x[0], '__iter__') else x[0])))

答案 4 :(得分:0)

完全不推荐,但我很乐意编码。

from collections import deque
from operator import itemgetter

lst = ['a', 2, 'b', 1, 'c', 3]
is_str = [isinstance(e, str) for e in lst]
two_heads = deque(map(itemgetter(1), sorted(zip(is_str, lst))))
[two_heads.pop() if a_str else two_heads.popleft() for a_str in is_str]

答案 5 :(得分:0)

为什么我们不按升序对列表进行排序,但要确保数字在字母之前:

[D, D, L, L, L]    # L -> letter; # D -> digit 

我们可以通过这种方式实现这一目标:

>>> lst = [5, 'a', 'x', 3, 6, 'b']
>>> sorted(lst, key=lambda el: (isinstance(el, str), el))
[3, 5, 6, 'a', 'b', 'x']

然后我们从左到右查看原始数组,如果遇到数字,我们从排序数组的开头选择元素,否则从结尾开始。完整的详细解决方案将是:

def one_sort(lst):
    s = sorted(lst, key=lambda el: (isinstance(el, str), el))
    res = []
    i, j = 0, len(s)
    for el in lst:
        if isinstance(el, str):
            j -= 1
            res.append(s[j])
        else:
            res.append(s[i])
            i += 1
    return res

lst = [5, 'a', 'x', 3, 6, 'b']
print(one_sort(lst)) # [3, 'x', 'b', 5, 6, 'a']

更短但更神秘的解决方案将是:

def one_sort_cryptic(lst):
    s = sorted(lst, key=lambda el: (isinstance(el, str), el))
    return [s.pop(-isinstance(el, str)) for el in lst]

lst = [5, 'a', 'x', 3, 6, 'b']
print(one_sort_cryptic(lst)) # [3, 'x', 'b', 5, 6, 'a']