【问题标题】:Traversing a Python list and making in-place changes遍历 Python 列表并进行就地更改
【发布时间】:2016-03-06 06:04:45
【问题描述】:

我的任务是删除一个特定元素的所有实例(本例中为“6”)并将它们移到列表的末尾。要求是遍历列表进行内联更改(不创建补充列表)。

输入示例:[6,4,6,2,3,6,9,6,1,6,5] 输出示例:[4,2,3,9,1,5,6,6,6,6,6]

到目前为止,我只能通过制作补充列表(打破任务要求)来做到这一点,因此不允许使用此工作代码:

def shift_sixes(nums):
    b = []
    c = 0
    d = []

   for i in nums:
    if i == 6:
        b.insert(len(nums),i)
    elif i != 6:
        c = c +1
        d.insert(c,i)

   ans = d + b
   return ans

我也尝试过list.remove() 和list.insert(),但遇到了索引问题(当我insert() 然后将元素移动到末尾时它会移动):例如 -

a = [6,4,6,2,3,6,9,6,1,6,5]
  def shift_sixes(nums):
    for i in nums:
        if i == 6:
            nums.remove(i)
            nums.insert(nums[len(nums)-1], 0)
        elif i != 0:
            i
 shift_sixes(a)

另外,我尝试如下使用 enumerate() 函数,但在 b[idx] 赋值行的右侧遇到了问题:

for idx, b in enumerate(a):
     a[idx] = ???

已阅读其他 stackoverflow 条目 here、here 和 here,但它们没有解决元素向一端移动的问题。

对于此列表遍历/就地切换问题的任何帮助,我们将不胜感激。非常感谢。


编辑 @eph - 谢谢。这确实是一个优雅的回应。我确定它会通过我的“无新列表”要求吗?我肯定打算更多地了解 lambda 及其用途 @falsetru - 感谢您提醒附加/弹出组合(我试图在我的原始查询中通过 list.remove() 和 list.insert() @tdelaney - 也谢谢你。不知何故,您的回答最接近我的尝试,但似乎没有通过 [0, 0, 5] 的测试。

【问题讨论】:

  • 对于就地替换选项的时序比较,请查看answer

标签: python list python-3.x


【解决方案1】:

反向迭代列表,pop元素如果是6,则append它。

xs = [6,4,6,2,3,6,9,6,1,6,5] 

for i in range(len(xs)-1, -1, -1):  # 10 to 0
    if xs[i] == 6:
        xs.append(xs.pop(i))

【讨论】:

  • 我喜欢它,但我们最终可能会对“在线”的含义产生疑问!
  • @tdelaney,我认为它用于就地。
【解决方案2】:

为什么不尝试这样的事情呢?

基本上,方法是首先计算值的数量。 如果为 0,则返回(因为如果为不在列表中的元素调用 list.index 方法,Python 会产生 ValueError)。

然后我们可以将值的第一个可接受索引设置为列表的长度减去它在列表中出现的次数。

然后我们可以结合 list.pop/list.append 来遍历列表,直到所有想要的值都出现在列表的末尾。

def shift_value(lst, value):
    counts = lst.count(value)       # 5
    if not counts:
        return lst

    value_index = len(lst) - counts
    index = lst.index(value)
    while index != value_index:
        lst.append(lst.pop(index))
        index = lst.index(value)
    return lst

lst = [6,4,6,2,3,6,9,6,1,6,5]
print(shift_value(lst, 6))

编辑:这是非常低效的,上面建议的更好的答案。 这需要 O(n^2) 时间,而不是 O(n) 时间。

【讨论】:

    【解决方案3】:

    遍历时修改列表是个坏主意。您可以制作一个副本进行遍历,也可以在遍历期间生成一个新列表。

    其实这个问题可以做很多方式,比如:

    >>> a.sort(key = lambda i: i == 6)
    >>> a
    [4, 2, 3, 9, 1, 5, 6, 6, 6, 6, 6]
    

    【讨论】:

    • 这会创建违反“家庭作业很奇怪”规则的新列表。
    • tdelaney 你是对的,但 list.sort 似乎可用:)
    • 喜欢,其实很优雅
    【解决方案4】:

    这里的关键词是“在线”。这样做的方法是将每个i 的num[i] = num[i+1] 移动到列表的末尾。

    def shift_sixes(num):
        for i, val in enumerate(num):
            if val == 6:
                # shift remaining items down
                for j in range(i,len(num)-1):
                    num[j] = num[j+1]
                # add 6 at the end
                num[-1] = 6
        return num
    
    print(shift_sixes([1,9,4,6,2,7,8,6,2,2,6]))
    print(shift_sixes([1,2,3]))
    print(shift_sixes([6]))
    print(shift_sixes([3]))
    

    【讨论】:

      【解决方案5】:

      使用两个跑步者。首先从头到尾检查 6s,然后从头到尾指向最后一个不是 6 的项目。继续交换 (a[i+1], a[i] = a[i], a[i+1]) 直到他们相遇。

      Catch:这不像稳定排序那样稳定。但我不认为这是一项要求。

      在带有键盘的 Python 解释器前会尝试编写工作代码。

      【讨论】:

        【解决方案6】:

        如果您需要稳定的排序(即不是 6 的元素的顺序应该保持不变),那么解决方案是:

        def move_to_end(data, value):
            current = 0 # Instead of iterating with for, we iterate with index
            processed = 0 # How many elements we already found and moved to end of list
            length = len(data) # How many elements we must process
            while current + processed < length: # While there's still data to process
                if data[current] == value: # If current element matches condition
                    data.append(data.pop(current)) # We remove it from list and append to end
                    processed += 1 # Our index remains the same since list shifted, but we increase number of processed elements
                else: # If current element is not a match
                    current += 1 # We increase our index and proceed to next element
        
        if __name__ == '__main__':
        
            print
            print 'Some testing:'
            print
        
            for test_case in (
                [1, 9, 4, 6, 2, 7, 8, 6, 2, 2, 6], # Generic case
                [6, 6, 6, 6], # All items are 6
                [1, 7], # No items are 6
                [], # No items at all
            ):
                print 'Raw:', test_case
                move_to_end(test_case, 6)
                print 'Becomes:', test_case
                print
        

        请注意,此解决方案不仅保留了不匹配元素的顺序,还保留了匹配元素的顺序。例如,如果您将检查条件从“等于 6”更改为“是偶数”,则所有符合条件的元素将被移至列表末尾,同时保持它们之间的顺序。

        【讨论】:

          【解决方案7】:

          为什么不保持简单?

          a = [6,4,6,2,3,6,9,6,1,6,5]
          def shift_sixes(nums):
              for i in range(0,len(nums)):
                  if nums[i] == 6:
                      nums.append(nums.pop(i))
          
          >>> shift_sixes(a)
          >>> a
          [3, 9, 1, 5, 2, 4, 6, 6, 6, 6]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-06
            • 2018-09-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-06
            相关资源
            最近更新 更多