Python进阶强化训练之数据结构与算法进阶

转载博客地址:https://ansheng.me/

内容源自刘硕老师的《Python进阶强化训练》

视频课程:http://coding.imooc.com/class/62.html


如何在列表、字典、集合中根据条件筛选数据?

实际问题

  1. 过滤列表中的负数

  2. 筛选出字典种值高于90的项

  3. 筛选出集合种能被3整出的元素

围绕上面三个问题我们来进行讨论,比如下面有一个列表:

1
2
3
4
>>>  from  random  import  randint
>>> li  =  [randint( - 10 10 for  in  range ( 10 )]
>>> li
[ - 10 - 9 1 10 - 3 - 7 - 6 - 7 4 - 5 ]

我们常规的做法就是通过for循环对列表中的每一个值进行迭代,然后判断如果值大于等于0,就确定这个值是一个整数,否则就丢弃,比如下面的代码:

1
2
3
4
5
6
7
8
9
>>> result  =  []
>>>  for  in  li:
# 如果这个元素大于等于0
...    if  n > =  0 :
# 加入的result列表中
...     result.append(n)
...
>>> result
[ 1 10 4 ]

实例

本篇所有的代码均在Python 3.5.x种运行,如果你使用的是python 2.7.x,那么请自行测试,在此之前,请导入一下模块用于测试:

1
2
3
4
# 用于生成随机数
>>>  from  random  import  randint
# 准确测量小段代码的执行时间
>>>  import  timeit

请仔细阅读下面的代码,看完后你将会有不一样的收获。

列表

  • filter函数

生成一个随机列表

1
2
3
>>> li  =  [randint( - 10 10 for  in  range ( 10 )]
>>> li
[ 6 - 8 9 3 3 8 9 - 4 9 - 6 ]
1
2
3
4
5
6
7
8
9
10
11
12
# x=列表中的一个元素,有多少个元素就迭代多少次
>>> result  =  filter ( lambda  x: x > = 0 , li)
>>>  for  in  result:
...   print (n)
...
6
9
3
3
8
9
9

  • 列表解析

生成一个随机列表

1
2
3
>>> li  =  [randint( - 10 10 for  in  range ( 10 )]
>>> li
[ 8 - 5 - 2 8 9 4 - 6 - 5 5 4 ]
1
2
>>> [x  for  in  li  if  x > = 0  ]
[ 8 8 9 4 5 4 ]

  • filter与列表解析性能对比

使用filter执行时间

1
2
>>> timeit.Timer( 'filter(lambda x: x >=0, [4, -1, 1, 3, -10, 5, -8, 0, 6, 3])' ).timeit()
0.38938787838583266

使用列表解析执行时间

1
2
>>> timeit.Timer( '[x for x in [4, -1, 1, 3, -10, 5, -8, 0, 6, 3] if x >=0 ]' ).timeit()
1.1142896312373978

通过以上的测试可以看出filter的执行时间明显比列表解析要快些,当然这并不是一个非常准确的数字,还是有待考察的。

字典

先随机生成一个字典:

1
2
3
>>> dic  =  { x: randint( 60 100 for  in  range ( 1 21 ) }
>>> dic
{ 1 61 2 75 3 69 4 70 5 79 6 90 7 74 8 85 9 77 10 86 11 93 12 96 13 86 14 79 15 60 16 84 17 70 18 72 19 61 20 87 }

  • 字典解析

1
2
>>> { k: v  for  k, v  in  dic.items()  if  v >  90  }
{ 11 93 12 96 }

集合

生成一个集合:

1
2
3
4
>>> li  =  [randint( - 10 10 for  in  range ( 10 )]
>>> s  =  set (li)
>>> s
{ 0 1 3 4 7 - 9 - 8 }

  • 集合解析

1
2
>>> { x  for  in  if  %  3  = =  0  }
{ 0 3 - 9 }

如何为元组中的每个元素命名、提高程序可读性?

实际问题

某校的学生信息系统中的数据存储格式如下:

1
(名字,年龄,性别,邮箱地址)

比如有如下学生信息:

1
2
3
4
student1  =  ( 'Hello' 15 'Schoolboy' 'hello@gmail.com' )
student2  =  ( 'World' 16 'Girls' 'World@gmail.com' )
student3  =  ( 'ansheng' 20 'Schoolboy' 'anshengme.com@gmail.com' )
.....

通常我们会以如下方式进行取值:

1
2
3
4
>>> student1[ 2 ]
'Schoolboy'
>>> student1[ 3 ]
'hello@gmail.com'

在代码比较多的情况下,使用大量的索引进行访问会降低程序的可读性,如何解决这个问题呢?

方案1

定义类似于其他语言的枚举类型,也就是定义一系列的数值常量.

1
2
3
4
5
6
7
8
9
10
11
# 创建一个学生
>>> student  =  ( 'ansheng' 20 'Schoolboy' 'anshengme.com@gmail.com' )
# 定义常量
>>> NAME, AGE, SEX, EMAIL  =  range ( 4 )
# 通过常量进行取值
>>> student[NAME]
'ansheng'
>>> student[AGE]
20
>>> student[EMAIL]
'anshengme.com@gmail.com'

方案2

使用标准库中的collections.namedtuple替代内置的tuple

1
2
3
4
5
6
7
8
9
10
11
12
>>>  from  collections  import  namedtuple
>>> Student  =  namedtuple( 'Student' , [ 'name' , 'age' , 'sex' , 'email' ])
>>> s  =  Student( 'ansheng' 20 'Schoolboy' 'anshengme.com@gmail.com' )
>>> s
Student(name = 'ansheng' , age = 20 , sex = 'Schoolboy' , email = 'anshengme.com@gmail.com' )
# 使用属性进行访问
>>> s.name
'ansheng'
>>> s.age
20
>>> s.sex
'Schoolboy'

stuple的一个子类

1
2
>>>  isinstance (s,  tuple )
True

如何统计序列中元素的出现频度?

实际问题

  1. 某随机的列表中,找出出现次数最高的三个元素,他们的出现次数是多少?

  2. 对某英文文章的单词进行词频统计,找出出现次数最高的10个单词,他们出现的次数是多少?

解决方案

使用collections.Counter对象,将序列传入Counter的构造器,得到Counter对象是元素频度的字典,Counter.most_common(n)方法得到频度最高的n个元素的列表

实例

  • 常规的解决方法

生成随机序列的列表

1
2
3
4
>>>  from  random  import  randint
>>> li  =  [randint( 0 20 for  in  range ( 30 )]
>>> li
[ 14 11 10 13 19 10 3 17 12 13 18 5 10 1 9 17 1 8 3 15 8 3 20 10 9 20 6 13 8 20 ]

以字典的形式创建每个数字出现的次数,

1
2
3
4
# 默认的出现次数为0
>>> count  =  dict .fromkeys(li,  0 )
>>> count
{ 1 0 3 0 5 0 6 0 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 0 17 0 18 0 19 0 20 0 }

每遇到一个x(列表中的数),就去字典中让值+1

1
2
3
4
5
>>>  for  in  li:
...  count[x]  + =  1
...
>>> count
{ 1 2 3 3 5 1 6 1 8 3 9 2 10 4 11 1 12 1 13 3 14 1 15 : 1 17 2 18 1 19 1 20 3 }

然后循环count找到最大的三个数字,取出来就好。

  • 使用collections.Counter对象

1
2
# 导入Counter
>>>  from  collections  import  Counter
1
2
3
4
5
>>> count  =  Counter(li)
>>> count
Counter({ 10 4 3 3 8 3 13 3 20 3 1 2 9 2 17 2 5 1 6 1 11 1 12 1 14 1 15 1 18 1 19 1 })
>>> count.most_common( 3 )
[( 10 4 ), ( 3 3 ), ( 8 3 )]

  • 英文文章词频统计实例

1
2
3
4
5
6
7
8
>>>  from  collections  import  Counter
>>>  import  re
>>> txt  =  open ( 'jquery.cookie.js' ).read()
>>> count  =  Counter(re.split( '\W+' , txt))
>>> count
Counter({ 's' 20 'cookie' 18 'options' 16 'value' 12 'function' 11 'key' 10 'var' 10 'return' 9 'expires' 8 'if' 8 'config' 8 't' 6 'result' 5 'the' 5 'a' 5 'it' 5 '1' 4 'decode' 4 'i' 4 'converter' 4 'factory' 4 'undefined' 4 'cookies' 4 'read' 3 'domain' 3 'g' 3 'encode' 3 'raw' 3 'path' 3 'name' 3 'replace' 3 'typeof' 3 'parts' 3 'we' 3 'define' 3 'document' 3 'is' 3 'pluses' 3 'jquery' 3 'If' 3 , ' ': 2, ' else ': 2, ' for ': 2, ' object ': 2, ' can ': 2, ' json ': 2, ' join ': 2, ' days ': 2, ' not ': 2, ' jQuery ': 2, ' in ': 2, ' l ': 2, ' parse ': 2, ' ignore ': 2, ' split ': 2, ' isFunction ': 2, ' unusable ': 2, ' stringifyCookieValue ': 2, ' secure ': 2, ' extend ': 2, ' decodeURIComponent ': 2, ' parseCookieValue ': 2, ' JSON ': 2, ' 0 ': 2, ' defaults ': 2, ' extending ': 1, ' storing ': 1, ' Copyright ': 1, ' thus ': 1, ' use ': 1, ' place ': 1, ' require ': 1, ' max ': 1, ' length ': 1, ' according ': 1, ' AMD ': 1, ' carhartl ': 1, ' first ': 1, ' globals ': 1, ' catch ': 1, ' https ': 1, ' are ': 1, ' try ': 1, ' Write ': 1, ' spaces ': 1, ' written ': 1, ' array ': 1, ' age ': 1, ' supported ': 1, ' attribute ': 1, ' under ': 1, ' exports ': 1, ' that ': 1, ' Klaus ': 1, ' RFC2068 ': 1, ' Replace ': 1, ' as ': 1, ' shift ': 1, ' Prevent ': 1, ' 864e ': 1, ' slice ': 1, ' prevents ': 1, ' stringify ': 1, ' loop ': 1, ' e ': 1, ' at ': 1, ' v1 ': 1, ' 5 ': 1, ' com ': 1, ' when ': 1, ' toUTCString ': 1, ' setTime ': 1, ' CommonJS ': 1, ' false ': 1, ' amd ': 1, ' Plugin ': 1, ' quoted ': 1, ' couldn ': 1, ' an ': 1, ' no ': 1, ' github ': 1, ' argument ': 1, ' Must ': 1, ' license ': 1, ' second ': 1, ' break ': 1, ' Browser ': 1, ' IE ': 1, ' Date ': 1, ' to ': 1, ' prevent ': 1, ' To ': 1, ' Released ': 1, ' by ': 1, ' with ': 1, ' Cookie ': 1, ' Also ': 1, ' indexOf ': 1, ' there ': 1, ' side ': 1, ' MIT ': 1, ' odd ': 1, ' case ': 1, ' number ': 1, ' encodeURIComponent ': 1, ' calling ': 1, ' Hartl ': 1, ' unescape ': 1, ' all ': 1, ' removeCookie ': 1, ' Read ': 1, ' new ': 1, ' assign ': 1, ' fresh ': 1, ' server ': 1, ' 2013 ': 1, ' String ': 1, ' empty ': 1, ' 4 ': 1, ' This ': 1, ' alter':  1 })
>>> count.most_common( 10 )
[( 's' 20 ), ( 'cookie' 18 ), ( 'options' 16 ), ( 'value' 12 ), ( 'function' 11 ), ( 'key' 10 ), ( 'var' 10 ), ( 'return' 9 ), ( 'expires' 8 ), ( 'if' 8 )]

如何根据字典中值的大小, 对字典中的项排序?

实际问题

某班英语成绩以字典形式进行存储,格式为:

1
2
3
4
5
6
{
    'ansheng' 79 ,
    'Jim' 66 ,
    'Hello' 99 ,
    ...
}

要求根据成绩高低,计算学生排名。

解决方案

使用内置函数sorted(),但是默认情况下sorted()并不能对字典进行排序,这里提供了两种解决方法:

  1. 利用zip()将字典数据转化为元组然后把值传给sorted()进行排序

  2. 传递sorted()函数的key参数

实例

先创建一个成绩单:

1
2
3
4
>>>  from  random  import  randint
>>> Transcripts  =  { x: randint( 60 , 100 for  in  'xyzabc'  }
>>> Transcripts
{ 'z' 61 'x' 74 'b' 81 'c' 65 'y' 88 'a' 98 }

使用sorted()进行排序的时候是以字典的key进行的

1
2
>>>  sorted (Transcripts)
[ 'a' 'b' 'c' 'x' 'y' 'z' ]

  • 第一种解决方法

获取字典的所有建

1
2
>>> Transcripts.keys()
dict_keys([ 'z' 'x' 'b' 'c' 'y' 'a' ])

获取字典所有的值

1
2
>>> Transcripts.values()
dict_values([ 61 74 81 65 88 98 ])

通过zip()把字典转换为元组

1
>>> T  =  zip (Transcripts.values(), Transcripts.keys())

通过sorted()进行排序得到结果

1
2
>>>  sorted (T)
[( 61 'z' ), ( 65 'c' ), ( 74 'x' ), ( 81 'b' ), ( 88 'y' ), ( 98 'a' )]

元组在进行比较的时候是先从第一个元素进行比较,如果比较值为True,则后面的就不进行比较:

1
2
3
4
5
>>> ( 100 'a' ) > ( 50 'b' )
True
>>> ( 50 'a' ) > ( 50 'b' )
# a不大于b,返回False
False

  • 第二种解决方法

1
2
3
4
5
>>> Transcripts.items()
dict_items([( 'z' 61 ), ( 'x' 74 ), ( 'b' 81 ), ( 'c' 65 ), ( 'y' 88 ), ( 'a' 98 )])
# key需要传入一个函数,每次迭代Transcripts.items()的时候,把第一个元素传入进去,然后进行排序
>>>  sorted (Transcripts.items(), key = lambda  x: x[ 1 ])
[( 'z' 61 ), ( 'c' 65 ), ( 'x' 74 ), ( 'b' 81 ), ( 'y' 88 ), ( 'a' 98 )]

如何快速找到多个字典中的公共键(key)?

在每一个字典中都会出现的键称之为公共键

1
2
3
4
>>>  from  random  import  randint, sample
# abcdefg是随机产生的key,每次随机取3-6个key
>>> sample( 'abcdefg' , randint( 3 6 ))
[ 'g' 'f' 'c' 'a' 'e' 'd' ]

生成随机的字典

1
2
3
4
5
6
7
8
9
>>> s1  =  { x: randint( 1 4 for  in  sample( 'abcdefg' , randint( 3 6 )) }
>>> s2  =  { x: randint( 1 4 for  in  sample( 'abcdefg' , randint( 3 6 )) }
>>> s3  =  { x: randint( 1 4 for  in  sample( 'abcdefg' , randint( 3 6 )) }
>>> s1
{ 'c' 3 'g' 1 'e' 2 }
>>> s2
{ 'a' 2 'f' 2 'g' 3 'e' 1 'd' 2 }
>>> s3
{ 'a' 2 'c' 2 'e' 2 'f' 4 }

解决方案

  • 传统的做法如下:

1
2
3
4
5
6
7
8
9
10
11
# 生成一个列表
>>> res  =  []
# 循环s1字典的所有键
>>>  for  in  s1:
# 如果键在s2和s3中都存在就添加到res列表中
...    if  in  s2  and  in  s3:
...     res.append(k)
...
>>> res
# 得到的键e,在s2和s3中都存在
[ 'e' ]

  • 利用集合(set)的交集操作

使用字典的keys()方法,得到一个字典的keys的集合

1
2
>>> s1.keys() & s2.keys() & s3.keys()
{ 'e' }

如何让字典保持有序?

解决方案

使用collections.OrderedDict,以OrderedDict替代内置字典Dict,依次将数据存入OrderedDict

1
2
# 导入OrderedDict模块
>>>  from  collections  import  OrderedDict
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建一个OrderedDict()对象
>>> dic  =  OrderedDict()
# 添加数据
>>> dic[ 'H1' =  ( 'a' 'b' )
>>> dic[ 'H2' =  ( 'ab' 'cd' )
>>> dic[ 'H3' =  ( 'ww' 'ss' )
# 变量字典中的数据
>>>  for  in  dic:  print (n)
...
H1
H2
H3
>>>  for  in  dic:  print (n)
...
H1
H2
H3

如何实现用户的历史记录功能(最多n条)?

解决方案

可以使用容量为n的队列存储历史纪录,使用标准库collections中的deque,它是一个双端循环队列。

如果要保存到文件中,可以在程序堆出前,可以使用pickle将队列对象存入文件,再次运行程序时将其导入。

  • 小例子

制作一个简单的猜数字小游戏,添加历史纪录的功能,显示用户最近猜过的数字,限定最近5条。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导入deque模块
>>>  from  collections  import  deque
# 创建一个队列,容量初始值为空,最多放5个值
>>> q  =  deque([],  5 )
>>> q
deque([], maxlen = 5 )
>>> q.append( 1 )
>>> q.append( 2 )
>>> q.append( 3 )
>>> q.append( 4 )
>>> q.append( 5 )
>>> q
deque([ 1 2 3 4 5 ], maxlen = 5 )
# 当超过5个值的时候,第一个就会被挤出去
>>> q.append( 6 )
>>> q
deque([ 2 3 4 5 6 ], maxlen = 5 )

实例的脚本文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from  random  import  randint
from  collections  import  deque
 
# 随机生成1-100的数字
=  randint( 1 100 )
# 创建一个队列histort,默认为空,最大限度值为5个
histort  =  deque([],  5 )
 
 
def  guess(k):
     # 数字猜对
     if  = =  N:
         print ( 'right' )
         return  True
     if  k < N:
         print ( '%s is less-than N'  %  (k))
     else :
         print ( '%s is greater-than N'  %  (k))
     return  False
 
 
while  True :
     line  =  input ( 'please input a number: ' )
     if  line.isdigit():
         =  int (line)
         histort.append(k)
         if  guess(k):
             break
     # 如果输入'history'就输出队列中的内容
     elif  line  = =  'history' :
         print ( list (histort))

转载博客地址:https://ansheng.me/

内容源自刘硕老师的《Python进阶强化训练》

视频课程:http://coding.imooc.com/class/62.html


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python编程入门与算法进阶》是一本针对初学者的Python编程教材,适合想要入门Python的读者。这本书内容涵盖了Python基础知识、语法和常用库的使用,还包括了一些常见的算法和数据结构的介绍和实现。 在该书的前半部分,作者详细讲解了Python的基础知识,包括Python的安装与配置、变量和数据类型、流程控制语句、函数和模块、文件操作等内容。通过这些章节的学习,读者可以掌握Python的基本语法和常用操作,为后续的学习打下基础。 在书的后半部分,作者介绍了Python中常用的库,如NumPy、Pandas、Matplotlib等。通过学习这些库的使用,读者可以进行数据分析、可视化等操作。此外,书中还介绍了一些常见的算法和数据结构,如排序算法、树、图等,读者可以通过实现这些算法来提升自己的编程能力。 该书的特点是结合理论和实践,通过丰富的代码示例和练习题,帮助读者巩固所学知识,并提高编程能力。此外,书中还提供了一些实际的项目案例,供读者在学习过程中实践应用。 总之,《Python编程入门与算法进阶》是一本很好的Python编程教材,适合想要入门Python并提升编程能力的读者阅读。无论是初学者还是有一定编程基础的人,都可以通过这本书快速掌握Python的基本知识和常用操作,并在此基础上学习算法和数据结构的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值