简体   繁体   English

从中间对列表进行排序的最 Pythonic 方式?

[英]Most Pythonic way to sort a list from middle?

I have a list of integers.我有一个整数列表。 Now I want to sort from the middle.现在我想从中间排序。 By this I mean:我的意思是:

  1. starts from the number in the middle if the length of the list is an odd number;如果列表的长度是奇数,则从中间的数字开始;
  2. starts from the larger of the two numbers in the middle if the length of the list is an even number;如果列表的长度是偶数,则从中间两个数字中的较大者开始;
  3. alternatingly take the numbers on the left and right (or right and left, depending on their magnitudes).交替取左右(或左右,取决于它们的大小)的数字。 Always first take the larger of the two.总是先取两者中较大的一个。

I managed to write the following code, but only works for odd-size lists.我设法编写了以下代码,但仅适用于奇数大小的列表。 It will not be hard to write an even version for this, but I feel like there has to be more general and "Pythonic" way to code this.为此编写一个偶数版本并不难,但我觉得必须有更通用和“Pythonic”的方式来编码。 I'm actually surprised that this trivial question has never been asked before.实际上,我很惊讶以前从未有人问过这个微不足道的问题。

# Odd length, works
lst = [1, 3, 5, 7, 9, 10, 8, 6, 4, 2, 0] # Expected output [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

# Even length, doesn't work
#lst = [1, 3, 5, 7, 9, 8, 6, 4, 2, 0] # Expected output [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

mid = len(lst) // 2
dist = 1
res = [lst[mid]]
while dist <= mid:
    res += sorted([lst[mid - dist], lst[mid + dist]], reverse=True)
    dist += 1
assert len(res) == len(lst)
print(res)

Note that the sequence after the first number must follows the distance +-1, +-2, +-3, ... For example, the expected output of [5, 4, 3, 2, 1] is [3, 4, 2, 5, 1] ;注意,第一个数字后面的序列必须遵循距离+-1,+-2,+- [5, 4, 3, 2, 1] 5,4,3,2,1]的预期output是[3, 4, 2, 5, 1] ; the expected output of [5, 3, 1, 0, 2, 4] is [1, 0, 3, 2, 5, 4] [5, 3, 1, 0, 2, 4]的预期 output 是[1, 0, 3, 2, 5, 4]

Also, it would be nice if someone could provide the function with the option of reverse=True or False , so that user can decide whether to pick larger or smaller number first.此外,如果有人可以为 function 提供reverse=TrueFalse选项,那就太好了,这样用户就可以决定是先选择更大还是更小的数字。

Try the following (although I prefer the recursive version below):尝试以下(虽然我更喜欢下面的递归版本):

lst_odd = [1, 3, 5, 7, 9, 10, 8, 6, 4, 2, 0]
lst_even = [1, 3, 5, 7, 9, 8, 6, 4, 2, 0]

def sorted_from_middle(lst, reverse=False):
    left = lst[len(lst)//2-1::-1]
    right = lst[len(lst)//2:]
    output = [right.pop(0)] if len(lst) % 2 else []
    for t in zip(left, right):
        output += sorted(t, reverse=reverse)
    return output

print(sorted_from_middle(lst_odd)) # [10, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1]
print(sorted_from_middle(lst_even)) # [8, 9, 6, 7, 4, 5, 2, 3, 0, 1]
print(sorted_from_middle(lst_odd, True)) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(sorted_from_middle(lst_even, True)) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(sorted_from_middle([5, 4, 3, 2, 1], True)) # [3, 4, 2, 5, 1]
print(sorted_from_middle([5, 3, 1, 0, 2, 4], True)) # [1, 0, 3, 2, 5, 4]

The idea is to cut the input list into two parts, left and right ;这个想法right将输入列表分成left两部分; make them have the equal length by first appending the center-most item, if any, to the output list (in the code this is done by stealing an item from right ).通过首先将最中心的项目(如果有)附加到 output 列表(在代码中这是通过从right窃取项目来完成),使它们具有相同的长度。 Let left be reversed, so that in both left and right the first element is the most closest to the center.left反转,以便在leftright中,第一个元素最靠近中心。 Then use zip() to simultaneously take two elements with the distance 1 from the center, compare them, and append them accordingly to the output list.然后使用zip()同时取两个与中心距离为 1 的元素,比较它们,并根据 output 列表对它们进行 append。 Then move to the next pair of items and so on.然后移动到下一对项目,依此类推。


As a side note, you can do this also with a recursive function, which handles even/odd cases more cleanly in my opinion.作为旁注,您也可以使用递归 function 来执行此操作,我认为它可以更干净地处理偶数/奇数情况。

def sorted_from_middle(lst, reverse=False):
    if len(lst) <= 1:
        return lst
    tail = sorted([lst[-1], lst[0]], reverse=reverse)
    return sorted_from_middle(lst[1:-1], reverse) + tail

The idea is basically just 1) excluding the middle element if it's odd and saving it, then 2) sort each pair made by one element of the first half of the remaining list organized backwards and another from the second half of the remaining list;这个想法基本上只是 1)排除中间元素,如果它是奇数并保存它,然后 2)对由剩余列表前半部分的一个元素向后组织和剩余列表后半部分的另一个元素组成的每一对进行排序; and 3) flatten the list of pairs adding the former middle element as the first one (if any)和 3) 展平将前中间元素添加为第一个元素的对列表(如果有)

import numpy


lst1 = [1, 3, 5, 7, 9, 10, 8, 6, 4, 2, 0]
lst1_out = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
lst2 = [1, 3, 5, 7, 9, 8, 6, 4, 2, 0]
lst2_out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
lst3 = [5, 4, 3, 2, 1]
lst3_out = [3, 4, 2, 5, 1]
lst4 = [5, 3, 1, 0, 2, 4]
lst4_out = [1, 0, 3, 2, 5, 4]

def sort_from_middle(lst,reverse=True):
    lst = lst.copy()
    N = len(lst)
    if N == 1:
        return lst
    elif N == 2:
        return sorted(lst, reverse=reverse)
    else:
        flatten_func = lambda t: [item for sublist in t for item in sublist]
        n = int(numpy.floor(N/2.0))
        x0 = [ lst.pop(n) ] if ((N%2) == 1) else []
        N = len(lst)
        return x0 + flatten_func([ sorted([x1,x2],reverse=reverse) for x1,x2 in zip(lst[int(N/2)-1::-1],lst[int(N/2):]) ])
    

print(lst1)
print(sort_from_middle(lst1))
print(lst1_out)

print(' \n')
print(lst2)
print(sort_from_middle(lst2))
print(lst2_out)

print(' \n')
print(lst3)
print(sort_from_middle(lst3))
print(lst3_out)

print(' \n')
print(lst4)
print(sort_from_middle(lst4))
print(lst4_out)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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