【啃书】Python数据结构与算法分析(第二版)---导论
前言
算法
- 计算机科学的研究对象是问题、解决问题的过程,以及通过该过程得到的解决方案。给定一个问题,计算机科学家的目标是开发一个能够解决该问题的算法。算法是具有有限步骤的过程,依照这个过程便能解决问题。因此算法就是解决方案
计算机科学
- 定义一:研究问题及解决方案,以及研究目前无解的问题的学科。
- 定义二:在描述问题及解决方案时,经常会用“可计算”一次。因此计算机科学也可以定义为:研究可计算以及不可计算的问题,即研究算法的存在性以及不存在性。
抽象
- 所谓抽象,就是将具体的问题,使用逻辑思维,将之提炼出来。举个例子,下面这个计算开平方的函数,别人已经实现了解决方案,我们只需要调用被人写好的接口:函数名,所需参数,了解返回值就可以用了。所有的计算细节都被隐藏起来了。
编程
- 编程是指通过编程语言将算法编码以使其能被计算机执行的过程。
为何学习算法
- 虽然解决问题的方案有很多,但总有一个较其他在内存上、效率上更好的策略,学习算法的意义就在于比较这些算法,学会分析技巧。
Python基础
- 面向对象的编程语言、解释型语言。动态语言。
数据类型
-
内建原子数据类型(int、float、bool)
-
整数类型int、浮点型数据float
标准数学运算符:+、-、*、/、以及**(幂运算)、%(取余)、//(整除)
-
布尔数据bool
布尔对象可能的状态值是True、False。布尔运算符:and、or、not。
布尔运算也被用于等于(==)、大于(>).... -
标识符
即变量名:数字、字符型、下划线。命名时不以数字开头,做到见名知意。
-
命名、定义
变量名在赋值的时候,存的是数据的引用,而不是数据本身。Python又是一个动态语言,当引用发生改变的时候,数据随之改变。
-
-
内建集合数据类型(有序:list、str、tuple;无序:set、dict)
-
列表
列表是异构的,意味着其内部元素的指向的数据对象不需要都是同一个类,并且这个集合可以赋值给一个变量。
- my_list = [1, 1.5, True, my_dict]
列表有序的,所以支持一系列的Python序列运算
- 索引(my_list[0])、连接(+)、重复(*)、成员(in)、长度(len)、切片([:])
注意点:切片运算:顾头不顾尾,重复运算:返回的结果是序列中指向数据对象的引用的重复。
my_list = [1, 2, 4, 6] print(my_list[1:3]) # [2, 4] A = my_list * 3 print(A) # [1, 2, 4, 6, 1, 2, 4, 6, 1, 2, 4, 6] B = [my_list] * 3 print(B) # [[1, 2, 4, 6], [1, 2, 4, 6], [1, 2, 4, 6]] my_list[0] = 5 print(B) # [[5, 2, 4, 6], [5, 2, 4, 6], [5, 2, 4, 6]],当my_list改变时,B随之改变
列表支持一些用于构建的数据类型的方法
- 末尾追加
mylist.append(item)
- 索引i处插入
mylist.insert(i, item)
- 删除并返回
删除末尾:my_list.pop()
、删除某索引i的元素:my_list.pop(i)
- 按大小排序
my_list.sort()
- 列表前后颠倒
my_list.reverse()
- 删除不返回
del my_list[i]
- 返回元素第一次出现的下标
my_list.index(item)
- 返回元素在列表出现的次数
my_list.count(item)
- 移除某元素第一次在列表出现
my_list.remove(item)
my_list = [1, 2, 4, 6] my_list.reverse() print(my_list) # [6, 4, 2, 1] print(my_list.pop()) # 1
范围对象
- range函数会生成一个代表值序列的范围对象。使用lsit可以看到其值。
my_range = range(10) print(my_range) # range(0, 10) print(list(my_range)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 顾头不顾尾 print(list(range(5, 10, 2))) # 5开始,到9结束,步长为2
-
字符串
常量字符串通过引号引起来,与标识符(变量区别开来)。字符串是不可变的,
my_str[0] = "a"
会报错。- 由于字符串是序列,因此前面提到的序列运算符(索引(my_list[0])、连接(+)、重复(*)、成员(in)、长度(len)、切片([:]))都能用
- 除此之外,字符串还有一些特殊地方法。
- 居中
my_str.center(w),返回一个新的字符串,使用空格填充,使其长度为w,并居中
- 统计次数
my_str.count(item),返回items出现的次数
- 向左靠齐
my_str.ljust(w),返回一个新的字符串,使用空格填充,使其长度为w,并使原字符靠右放置
- 向右靠齐
my_str.rjust(w)
- 转小写并返回
my_str.lower()
- 转大写并返回
my_str.upper()
- 查找item第一次出现的下标
my_str.find(item)
- 分割
my_str.split(schar),按schar将字符串切割,返回一个列表,schar为空时,默认使用制表符、换行符、空格等空白字符切割
- 去除首尾空格
my_str.strip()
- 居中
-
元组
元组与列表非常相似,都是异构数据序列。主要区别在于,元组和字符串一样不可修改。
- 同前面列表与字符串,可以使用序列运算符(索引(my_list[0])、连接(+)、重复(*)、成员(in)、长度(len)、切片([:]))
-
集合(set)
集合是由零个或多个不可修改的Python数据对象组成的无序集合。集合内不允许出现重复元素,集合也是异构的。
- 支持的运算:成员(in)、长度(len())、并集(|)、交集(&)、差集(-)、子集(<=)
- 内置方法:
- 并集
a_set.union(other_set),返回两者的交集
- 交集
a_set.intersection(other_set),返回两者的交集
- 差集
a_set.difference(other_set),返回a_set特有的元素
- 子集
a_set.issubset(other_set),判断a_set是否为other_set的子集
- 添加元素
a_set.add(item)
- 移除item元素
a_set.remove(item)
- 随机移除一个元素
a_set.pop()
- 清空集合
a_set.clear()
- 并集
-
字典
无序结构,key:value键值对组成。访问字典的语法与访问序列的语法十分相似,只是由之前的索引访问改成了key来访问。
- 注意:字典并不是通过key来进行有序维护的,键的位置是由散列来决定的。
- 支持的运算:key取值(my_dict[key]、成员运算(针对的是key)、删除(del my_dict[key])
- 内置方法:
- 取出所有的key
my_dict.keys()
- 取出所有的values
my_dict.values()
- 键值对以元组的形式返回
my_dict.items()
- get取值
my_dict.get(k),没有返回None,也可以设置默认返回值:my_dict.get(k, alt)
my_dict = {"name": "alan", "age": 18, "power": True} print(my_dict.keys()) # dict_keys(['name', 'age', 'power']) 返回的是对象 print(list(my_dict.keys())) # ['name', 'age', 'power'] print(my_dict.values()) # dict_values(['alan', 18, True]) 返回的是对象 print(my_dict.items()) # dict_items([('name', 'alan'), ('age', 18), ('power', True)]) 返回的是对象
- 取出所有的key
-
输入与输出
Pytho提供input函数用来接受用户输入的内容,返回的是一个字符串,print函数用于输出需要返回的内容。
- 格式化字符串
- 使用print函数
print(xxxx, seq="", end=""),seq控制分隔符,默认空格;end控制结尾,默认是换行符
- 占位符
"%s"
name = "alan" old = 18 price = 18.2 print("%s is %d years old!" % (name, old)) # alan is 18 years old! print("the book's price is %08.2f" % price) # the book's price is 00018.20
- format格式化
name = "alan" old = 18 print(f"{name} is {old} years old!") # alan is 18 years old! print("{} is {} years old!".format(name, old)) # alan is 18 years old!
- 使用print函数
控制结构(循环与分支)
while语句是非常普遍的循环结构。
while counter <= 10 and not done:
print("只有两者条件都为True的时候才会执行,但当counter<=10条件不满足的时候,后者条件就无需判断了。")
for循环语句一个应用场景是遍历一个有序集合(list、str、tuple)。另一个应用场景是进行有限次的迭代
for item in range(5)
分支:if-elif-else
异常处理
两种报错:语法错误(杜绝发生)、逻辑错误(避免发生),下面介绍的是针对逻辑错误(异常)的处理方法。
- try:
try下面将可能发生异常的代码块保护起来,用except取捕获异常。
- raise:
主动抛出异常,主动抛出的异常也会导致程序的终止,只是这个异常是我们手动创的。
函数
函数名、参数、函数体、返回值四者构成完整的函数。
def func(num):
root = num / 2
for item in range(20):
root = 1 / 2 * (root + (num / root))
return root
print(func(9)) # 3.0
print(func(81)) # 9.0
# 装饰器
from functools import wraps
def outfunc(func):
@warps(func) # 装饰器修复技术,用来隐瞒help函数查询的结果
def infunc(*args, **kwargs):
"""代码体,可以效验之类"""
get_return = func(*args, **kwargs)
"""代码体,可以对返回值进一步处理"""
return get_return
return infunc