for 使用注意
for 语句遍历容器类型或可迭代类型时,如果涉及到增加、删除元素,就需要小心。比如请先看下面的例子:
删除列表中的某个元素值,可能有重复,要求元素顺序不变,空间复杂度为O(1),如果像下面这样写就会有问题:
def delItems(nums, target):
for item in nums:
if item == target:
nums.remove(item)
return nums
对于大多数情况,上面的代码无法暴露出bug。但是考虑下面输入(特点:被删除的值连续出现):
r = delItems([2, 1, 3, 1, 1, 3], 1)
print(r)
打印结果为:[2,3,1,3]
对于刚接触编程的朋友对此很不解,为什么其中一个1
未被remove.
不管是Python, Java, C++,列表或数组删除元素时,其后面的元素都会逐次前移1位,但是for
依然会正常迭代,因此“成功”规避了相邻的后面元素1.
图形解释命中目标后的一些列动作:
下步最关键:解释器自动前移删除位置后的所有元素
但是,等到下一次迭代时,迭代器不等待,正常移动到下一个位置:
这样元素3
成功逃避是否与目标值相等的检查。
结论:命中目标处的后一个位置都逃避了是否与目标值相等的检查,所以一旦有连续目标值,必然就会漏掉,进而触发上面的bug.
明白上面这个原因后,重新再改写一遍删除所有重复元素的代码,下面代码不再使用for
直接遍历元素(再说一遍:增删元素原来迭代器发生改变,所以会导致异常行为),而是使用索引访问:
def delItems(nums, target):
i = 0
while i < len(nums):
if nums[i] == target:
del nums[i]
i -= 1
i += 1
return nums
r = delItems([2, 1, 3, 1, 1, 3], 1)
print(r) # [2,3,3]
如果元素等于target
,从数组nums
中删除nums[i]
,删除后解释器自动将i
后的元素都前移1位。据此,巧妙的控制i
值,一旦命中立即i
减去1,这样确保不漏检查。
《end》
送畅销书籍 Python网络爬虫 2本
送书方法:近一周分享最多的前2名,自动获得上面书籍。请于1天内联系小编(详情Python小例子公众号界面-点击联系我按钮,获得小编微信)。
如果想了解和购买这本书,可点击下面链接,现在当当有每满100减50的活动:
接下来持续会有送书,欢迎大家积极参与公众号的文章分享。原创不易,欢迎三连支持: