【问题标题】:Most Pythonic way to sort a list from middle?从中间对列表进行排序的大多数 Pythonic 方式?
【发布时间】:2021-06-13 06:20:58
【问题描述】:

我有一个整数列表。现在我想从中间排序。我的意思是:

  1. 如果列表长度为奇数,则从中间的数字开始;
  2. 如果列表长度为偶数,则从中间两个数字中较大的一个开始;
  3. 交替取左右(或左右,取决于它们的大小)的数字。总是先取两者中的较大者。

我设法编写了以下代码,但仅适用于奇数大小的列表。为此编写一个均匀的版本并不难,但我觉得必须有更通用和“Pythonic”的方式来编码。我真的很惊讶这个微不足道的问题以前从未被问过。

# 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)

请注意,第一个数字之后的序列必须遵循距离 +-1, +-2, +-3, ... 例如[5, 4, 3, 2, 1]的预期输出为[3, 4, 2, 5, 1][5, 3, 1, 0, 2, 4] 的预期输出是 [1, 0, 3, 2, 5, 4]

另外,如果有人能给函数提供reverse=TrueFalse的选项,那就太好了,这样用户就可以决定是先选择更大的数字还是先选择更小的数字。

【问题讨论】:

  • 我不明白问题 - 在这两个例子中,预期结果都可以通过简单的sorterd(list, revesed=True)
  • 不明白的看底部的两个例子
  • 也许你应该把每个部分单独排序,然后加入他们sorted(left) + sorted(right)
  • 好的。为了避免误解,我删除了分发的描述。我需要一个适用于随机整数列表的代码。
  • 似乎涉及heapq.merge(..., reverse=True)lst[:mid]lst[mid:] 的东西应该可以工作。

标签: python list sorting sequence


【解决方案1】:

试试下面的(虽然我更喜欢下面的递归版本):

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]

想法是将输入列表分成两部分,leftright;通过首先将最中心的项目(如果有)附加到输出列表来使它们具有相等的长度(在代码中,这是通过从right 窃取项目来完成的)。让left 倒转,这样leftright 中的第一个元素最靠近中心。然后使用zip()同时取两个离中心距离为1的元素,进行比较,并相应地追加到输出列表中。然后移动到下一对项目,依此类推。


附带说明,您也可以使用递归函数来执行此操作,在我看来,它可以更干净地处理偶数/奇数情况。

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

【讨论】:

  • 代码正在运行。只有一件事:据我了解,reverse 意味着从大到小排序。你能把你的代码改成相反的吗?
  • @ShaunHan 是的,当然!我已经编辑了代码。
【解决方案2】:

这个想法基本上只是 1) 如果中间元素是奇数则排除它并保存它,然后 2) 对由剩余列表的前半部分的一个元素向后组织的每一对进行排序,另一个从剩余的后半部分排序列表;和 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2018-11-30
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 2013-12-30
    • 1970-01-01
    相关资源
    最近更新 更多