Python 针对列表和字典比较del操作的性能

1. 文章概述

本文是根据《Python数据结构与算法分析》(第2版) 第二章算法分析的编程练习题“针对列表和字典比较del操作的性能”此题给出自己的解答,如有问题欢迎提出。

Python版本:3.9   系统:win10  设备:ASUA Vivobook 无畏pro14

2. 解答角度

回答从三个角度展开:

① del 对象最后一个元素

② del 整个对象

③ del 随机索引位置元素(执行1000次)

3. 所需知识

在数据结构研究算法的执行效率时,有两个角度分别为:算法占用的空间资源和算法的执行时间,算法的执行时间根据大O法进行衡量。为了得到算法的执行时间可以使用Python的第三方库timeit模块(无需安装)。而使用该模块,必须先创建一个Timer对象,该对象有两个参数:1:需要进行计时的Python语句;2.需要建立的测试的语句[1]。默认Timeit对象执行100万次,但这样会导致计算机高负荷运行很长时间,这里只是做个测试,因此规定执行1000次。执行次数根据Timer对象参数number进行设置。

本题Timer对象所需语法知识:

Timer("所需测试语句","将测试对象从__main__命名空间导入到timeit设置的计时的命名空间") [1]

4.实现

① del 对象最后一个元素
from timeit import Timer
from random

print("i     del lst[-1]  del dic[end]")  # 结构属性指标,方便看

for i in range(10000, 1000001, 20000):    # 假设问题规模从10000到1000001开始

    t1 = Timer("del x[-1]",
               "from __main__ import x")   # Timer 对象(列表),类似导入第三方库
    x = list(range(i))                     # 生成列表对象,注意这种生成方式执行时间最短
    lt = t1.timeit(number=1000)            # 调用Timer对象,执行1000次,并统计时间
    x = {j: None for j in range(i)}        # 生成字典对象,注意这种生成方式执行时间最短
    lst = list(x.keys())
    t = Timer("del x[lst[-1]]; lst.pop()",    # Timer对象(字典),注意对象参数一的执行命令
              "from __main__ import x, lst")
    dt = t.timeit(number=1000)
    print("%d %10.5f %10.5f" % (i, lt, dt))   # 输出结果
print()

执行结果(未全给出):

结论:无论是列表还是字典,del 最后一个元素是常数阶,即O(1),虽然删除了所有的元素,你可能想时间复杂度会是n阶,但由于是删除最后一个元素,并逐步向索引小的位置移动,这时就类似pop(),而字典和列表的pop()都是常数阶

     你能发现字典的执行时间比列表的执行时间大将近一倍,其实不然,由于我为了实现这个效果,我在字典的Timer对象中的调用命令中添加了一个lst.pop(),假设有更好的方法实现,那么字典执行时间将与列表一致。总结:字典和列表在本操作中的执行时间相同

② del 整个对象
print("i     del lst  del dic")
for i in range(10000, 1000001, 20000):
    t = Timer("del x",
              "from __main__ import x")   # 只创建一个Timer对象
    x = list(range(i))
    lt = t.timeit(number=1)
    x = {j: None for j in range(i)}
    dt = t.timeit(number=1)
    print("%d %10.7f %10.7f" % (i, lt, dt))
print()

执行结果:

结论:无论是列表还是字典,虽然执行时间有时候会抽风,但总体看del 整个对象(删除对象的引用)的时间复杂度为O(1),但需要注意的是删除列表中的所有元素(逐一)的时间复杂度为O(n)因为删除所有的元素,假设从索引为0开始删除,由于第一个元素被删除,列表后面的元素都要向前移动一个位置,所以时间复杂度如上所述。且这个操作的消耗的时间比以上del 对象最后一个元素的时间稍微长,可能因为是删除对象的引用吧。

③ del 随机索引位置元素(执行1000次)
print("i     del lst[random]  del dic[random]")
for i in range(10000, 1000001, 20000):
    x = list(range(i))
    t1 = Timer("del x[%d]" % random.randint(0, len(x)-1), # 注意参数一的执行设置
               "from __main__ import x")
    lt = t1.timeit(number=1000)
    x = {j: None for j in range(i)}
    t2 = Timer("del x[random.choice(list(x.keys()))]",   # 这里同样用了random模块
               "from __main__ import random, x")
    dt = t2.timeit(number=1000)
    print("%d %10.7f %10.7f" % (i, lt, dt))

执行结果:

结论:结果发现,虽然同样执行时间会抽风(可能跟计算机现在执行的其他程序的影响),但总体上列表和字典随机删除元素的操作的时间复杂度为O(n),且字典的时间复杂度随着问题规模的增大,两者相差的倍数也越大,且字典永远是最大的一个。事实是在问题规模为400000时,字典的执行时间已经超过4s了!

总体代码:

from timeit import Timer
from random

# 针对列表和字典比较del操作的性能
print("i     del lst[-1]  del dic[end]")
for i in range(10000, 1000001, 20000):
    t1 = Timer("del x[-1]",
               "from __main__ import x")
    x = list(range(i))
    lt = t1.timeit(number=1000)
    x = {j: None for j in range(i)}
    lst = list(x.keys())
    t = Timer("del x[lst[-1]]; lst.pop()",
              "from __main__ import x, lst")
    dt = t.timeit(number=1000)
    print("%d %10.5f %10.5f" % (i, lt, dt))
print()

# del 整个对象
print("i     del lst  del dic")
for i in range(10000, 1000001, 20000):
    t = Timer("del x",
              "from __main__ import x")
    x = list(range(i))
    lt = t.timeit(number=1)
    x = {j: None for j in range(i)}
    dt = t.timeit(number=1)
    print("%d %10.7f %10.7f" % (i, lt, dt))
print()

# del 随机索引
print("i     del lst[random]  del dic[random]")
for i in range(10000, 1000001, 20000):
    x = list(range(i))
    t1 = Timer("del x[%d]" % random.randint(0, len(x)-1),
               "from __main__ import x")
    lt = t1.timeit(number=1000)
    x = {j: None for j in range(i)}
    t2 = Timer("del x[random.choice(list(x.keys()))]",
               "from __main__ import random, x")
    dt = t2.timeit(number=1000)
    print("%d %10.7f %10.7f" % (i, lt, dt))

       最后值得说的就是几乎所有计算机老师都会说的,就是你的执行结果不一定跟我一样,请以自身所得结果为衡量标准。

参考文献

[1]《Python数据结构与算法分析》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python中的列表字典是两种常用的数据结构,它们的基本操作如下: 1. 列表的基本操作: - 创建列表:使用方括号[]或list()函数创建一个空列表,或者在方括号中添加元素创建一个非空列表。 - 访问列表元素:使用下标索引访问列表元素,下标从0开始。 - 修改列表元素:使用下标索引修改列表元素。 - 列表切片:使用[start:end]的方式获取列表的一个子列表,其中start表示起始下标,end表示结束下标(不包含)。 - 列表拼接:使用+运算符将两个列表拼接成一个新的列表。 - 列表重复:使用*运算符将一个列表重复n次。 - 列表长度:使用len()函数获取列表的长度。 - 列表排序:使用sort()方法对列表进行排序。 - 列表迭代:使用for循环对列表进行迭代。 2. 字典的基本操作: - 创建字典:使用大括号{}或dict()函数创建一个空字典,或者在大括号中添加键值对创建一个非空字典。 - 访问字典元素:使用键访问字典元素,键可以是数字、字符串、元组等不可变类型。 - 修改字典元素:使用键修改字典元素。 - 删除字典元素:使用del语句删除字典中的键值对。 - 字典长度:使用len()函数获取字典的长度。 - 字典迭代:使用for循环对字典进行迭代,可以使用items()方法获取键值对,keys()方法获取键,values()方法获取值。 下面是一些示例代码: 1. 列表操作示例: ```python # 创建列表 list1 = [1, 2, 3] list2 = list(range(4, 7)) # 访问列表元素 print(list1[0]) # 输出:1 # 修改列表元素 list1[0] = 0 print(list1) # 输出:[0, 2, 3] # 列表切片 print(list1[1:]) # 输出:[2, 3] # 列表拼接 list3 = list1 + list2 print(list3) # 输出:[0, 2, 3, 4, 5, 6] # 列表重复 list4 = list1 * 3 print(list4) # 输出:[0, 2, 3, 0, 2, 3, 0, 2, 3] # 列表长度 print(len(list1)) # 输出:3 # 列表排序 list5 = [3, 1, 2] list5.sort() print(list5) # 输出:[1, 2, 3] # 列表迭代 for i in list1: print(i) ``` 2. 字典操作示例: ```python # 创建字典 dict1 = {'name': 'Alice', 'age': 18} dict2 = dict([('name', 'Bob'), ('age', 20)]) # 访问字典元素 print(dict1['name']) # 输出:Alice # 修改字典元素 dict1['name'] = 'Cathy' print(dict1) # 输出:{'name': 'Cathy', 'age': 18} # 删除字典元素 del dict1['age'] print(dict1) # 输出:{'name': 'Cathy'} # 字典长度 print(len(dict1)) # 输出:1 # 字典迭代 for key, value in dict2.items(): print(key, value) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值