如何在适当的位置反转列表中的子列表?

Posted

技术标签:

【中文标题】如何在适当的位置反转列表中的子列表?【英文标题】:How do I reverse a sublist in a list in place? 【发布时间】:2014-04-11 00:32:58 【问题描述】:

我应该创建一个函数,它的输入是一个列表和两个数字,该函数反转子列表,它的位置由两个数字指示。 例如这是它应该做的:

>>> lst = [1, 2, 3, 4, 5] 
>>> reverse_sublist (lst,0,4) 
>>> lst  [4, 3, 2, 1, 5]

我创建了一个函数,它可以工作,但我不确定它是否到位。 这是我的代码:

def reverse_sublist(lst,start,end):
    sublist=lst[start:end]
    sublist.reverse()
    lst[start:end]=sublist
    print(lst)

【问题讨论】:

您可以使用id()检查它是否就位。 不,这实际上不是就地的,因为我们必须创建一个临时列表 (sublist) “就地”是模棱两可的。这可能意味着“改变输入对象”。这可能意味着“使用O(1) 额外的内存”。后者通常被称为“就地算法”,而前者通常被称为“就地反转列表”,所以你的作业的确切措辞可能会提供线索。 【参考方案1】:

...我不确定它是否到位。

...

lst[start:end]=sublist

是的,它已经到位。 lst 永远不会反弹,只是它的对象发生了变异。

【讨论】:

就是说,呃,不要就地修改东西:(【参考方案2】:

只需使用切片:

>>> lst = [1, 2, 3, 4, 5] 
>>> lst[0:len(lst[3::-1])]=lst[3::-1]
>>> lst
[4, 3, 2, 1, 5]

或者,也许更容易理解:

>>> lst = [1, 2, 3, 4, 5] 
>>> sl=lst[3::-1]
>>> lst[0:len(sl)]=sl
>>> lst
[4, 3, 2, 1, 5]

【讨论】:

这不是 OP 想要的——你正在破坏索引。你想做start = 0end = 3lst[start:end] = lst[end-1::-1] if start==0 else lst[end-1:start-1:-1],坦率地说,为了可读性,它应该被重写为OP一开始发布的函数! 以负步幅进行切片很棘手。 @adsmith 缺乏早晨咖啡和按钮触发手指之间的不匹配。固定的。正如 Ignacio Vazquez-Abrams 所说,在以负步幅进行切片时,您必须考虑一下。手指帮助。一旦你建立了正确的关系——效果很好。【参考方案3】:

没有临时列表的部分反转(如果您使用 Python 2,请将 range 替换为 xrange):

def partial_reverse(list_, from_, to):
    for i in range(0, int((to - from_)/2)):
        (list_[from_+i], list_[to-i]) = (list_[to-i], list_[from_+i])

list_ = [1, 2, 3, 4, 5, 6, 7, 8]
partial_reverse(list_, 3, 7)
print(list_)

【讨论】:

如果您尝试偶数长度,则会出错。例如。 partial_reverse(list_, 2, 7)==[1, 2, 8, 7, 5, 6, 4, 3]【参考方案4】:
def reverse_sublist(lst,start,end):
    lst[start:end] = lst[start:end][::-1]
    return lst

【讨论】:

在这种情况下,即使是最***的切片符号爱好者也可以屈尊使用reversed 吗? ;-) 我认为这需要lst[start:end+1]=lst[start:end+1][::-1],否则,正确的解决方案。 +1 @flakes 这会消耗内存 O(k) 吗? k=子字符串的长度。还是一一交换 O(1)? 这不是就地反转,它会在切片创建一个新列表时使用额外的内存。【参考方案5】:

lst[::-1] 是 Python 中反转列表的惯用方式,以下显示了它是如何以及它是就地的:

>>> lst = [1, 2, 3, 4, 5]
>>> id(lst)
12229328
>>> lst[:] = lst[::-1]
>>> lst
[5, 4, 3, 2, 1]
>>> id(lst)
12229328

【讨论】:

【参考方案6】:

试试crazy slicing,见Explain Python's slice notation和http://docs.python.org/2.3/whatsnew/section-slices.html

x = [1,2,3,4,5,6,7,8]

def sublist_reverse(start_rev, end_rev, lst):
    return lst[:end_rev-1:start_rev-1]+lst[:[end_rev]

print sublist_reverse(0,4,x)

[出]:

[8, 7, 6, 5, 4, 3, 2, 1]

【讨论】:

【参考方案7】:

我有两种in-place 反转的方法,简单的方法是中途循环列表,将元素与相应的mirror-elements 交换。 mirror-element 我的意思是 (first, last), (2nd, 2nd-last), (3rd, 3rd-last) 等等。

def reverse_list(A):
    for i in range(len(A) // 2): # half-way
        A[i], A[len(A) - i - 1] = A[len(A) - i - 1], A[i] #swap
    return A

另一种方式与上述类似,但使用递归而不是“循环”:

def reverse_list(A):
    def rev(A, start, stop):
        A[start], A[stop] = A[stop], A[start] # swap
        if stop - start > 1: # until halfway
            rev(A, start + 1, stop - 1)
        return A

    return rev(A, 0, len(A) - 1)

【讨论】:

【参考方案8】:

以部分或完整方式反转列表的最简单方法。

listVar = ['a','b','c','d']
def listReverse(list,start,end):
    while(start<end):
        temp = list[start]
        list[start] = list[end] #Swaping
        list[end]=temp
        start+=1
        end-=1
    print(list)


listReverse(listVar,1,3)

输出:- ['a', 'd', 'c', 'b']

【讨论】:

请注意,不需要临时变量,只需使用 oneliner:list[start], list[end] = list[end], list[start]【参考方案9】:
lst = [1,2,3,4,5,6,7,8]

假设您必须将第 2 个位置反转为第 4 个位置。

lst[2:5] = lst[2:5][::-1]

输出:

[1,2,5,4,3,6,7,8]

【讨论】:

请解释您的代码行,以便其他用户了解其功能。谢谢!【参考方案10】:

不确定您是否有与我类似的问题,但我需要在适当的位置反转一个列表。

我唯一缺少的是[:]

exStr = "String"

def change(var):
  var[:] = var[::-1] # This line here

print(exStr) #"String"
change(exStr)
print(exStr) #"gnirtS"

【讨论】:

【参考方案11】:

我进行了一个小实验,似乎对列表切片的任何分配都会导致内存分配:

import resource
resource.setrlimit(resource.RLIMIT_AS, (64 * 1024, 64 * 1024))

try:
    # Python 2
    zrange = xrange
    arr_size = 3 * 1024
except NameError:
    # Python 3
    zrange = range
    arr_size = 4 * 1024

arr = list(zrange(arr_size))

# We could allocate additional 100 integers, so there should be enough free memory
# to allocate a couple of variables for indexes in the statement below
#   additional_memory = list(zrange(100))

# MemoryError is raised here
arr[:] = zrange(arr_size)

所以你必须使用 for 循环来反转子列表。

PS:如果你想重复这个测试,你应该确保 setrlimit RLIMIT_AS 在你的平台上工作正常。此外,arr_size 可能会因不同的 python 实现而有所不同。

【讨论】:

【参考方案12】:

就地和常量内存两种方法:

def reverse_swap(arr, start=None, end=None):
    """
    Swap two edge pointers until meeting in the center.
    """

    if start is None:
        start = 0
    if end is None:
        end = len(arr)

    i = start
    j = end - 1
    while i < j:
        arr[i], arr[j] = arr[j], arr[i]
        i += 1
        j -= 1

def reverse_slice(arr, start=None, end=None):
    """
    Use python slice assignment but use a generator on the right-hand-side
    instead of slice notation to prevent allocating another list.
    """

    if start is None:
        start = 0
    if end is None:
        end = len(arr)

    arr[start:end] = (arr[i] for i in range(end - 1, start - 1, -1))

【讨论】:

【参考方案13】:

最简单的方法大概是使用切片赋值和reversed()

lst[start:end] = reversed(lst[start:end])

您也可以同时切片和反转,但这通常需要使用负索引或特殊处理start = 0时的情况,即:

lst[start:end] = lst[end-len(lst)-1:start-len(lst)-1:-1]

 lst[start:end] = lst[end-1::-1] if start == 0 else lst[end-1:start-1:-1]

OTOH,单独使用切片比reversed() 解决方案更快

【讨论】:

【参考方案14】:

更简洁的方式来做到这一点

a = [1,2,3,4,5,6,7,8,9]
i = 2
j = 7
while (i<j):
   a[i],a[j]=a[j],a[i]
   j -= 1
   i += 1
print(a)

【讨论】:

以上是关于如何在适当的位置反转列表中的子列表?的主要内容,如果未能解决你的问题,请参考以下文章

在Python中随机反转列表列表中的一半单词

如何反转模板工具包中的匿名列表?

如何反转多维数组中的重复值

如何在Android中的主/细分片段之间进行适当的导航?

Java数组列表反转

如何添加到 Thymeleaf/Spring Boot 发布请求中的子对象列表