数据结构
常见的数据结构有 线性表 、 链表 、 散列表 、队列、栈、 树 (二叉树、平衡二叉树、红黑树、BTree树、 B+Tree树) 、 图 等 ~~~
- 列表 list
- 元组 tuple
- 集合 set
- 字典 dict
列表 list
列表是一个线性表、特点: 用来存储多个数据的容器 , 空间地址连续、存储的数据 是
有序且可重复的。 线性表 可以 通过索引index进行数据的访问 、时间复杂度 O(1) , 添加数据 和 删除数据 效率较低 、时间复杂度为 O(n) n 代表列表的长度 。列表中的索引 支持 正索引 和 负索引 、 同一个位置的数据 它的正索引 和 负索引 满足一个公式:
正索引 - 负索引 = 列表的长度、列表长度可以使用 len 函数获取列表 是 可变的 可迭代对象
列表的创建方式
-
字面量定义方式
python# 创建 一个 空列表 ls = [] # 创建一个 包含指定内容的列表 , 数据之间使用 逗号分隔 ls = [1, 2, 3, 4, 5] # 创建一个包含 不同类型的数据 ls = [1, "hello", True, None, [1, 2, 3] ] # 创建一个列表、存储一些 相同的数据 ls = [1, 2, 3, 1, 2 ,3] print(ls) -
使用 list 工厂函数构建
python# 使用 list 工厂函数构建一个空列表 ls = list() string = "hello" # 将字符串转成 列表 ls = list(string) # 输出对应的列表数据 # list 工厂函数 可以将 任意的 可迭代对象 转成 列表 # 常见的可迭代对象有: 列表、 元组、 集合、 字典 、 字符串 、 range 对象 ls = list(range(10, 100)) print(ls) -
列表生成推导式
列表生成推导式的语法:
python[ exp for var in iterable if condition ] 语法: exp : 是一个表达式,是 最终 计算列表中数据 的表达式 var : 是一个变量、 代表 iterable 可迭代对象中的每一个元素 iterable : 是一个可迭代对象 condition : 是一个条件表达式,用来过滤保留满足条件的 var该语法中 , for 结构 可以出现 1 ~ N 次 , if 结构 可选, 如果有、则必须出现在 所有的 for 结构的 后面 ~~~
列表生成推导式的执行顺序是:
先执行 所有的 for 循环 (多个 for 采用 嵌套循环的方式进行执行) 、
在 最后一个 for 循环的内部 执行 if 条件 , 当 if 条件成立的时候,
保留 exp 执行的结果 。
python
# 快速生成一个列表
ls = [x + y for x in range(10) for y in range(10) if x == y]
# 输出列表中内容
print(ls)
列表的基本操作
- 添加数据
-
append(x) : 将 数据追加到 列表的 尾部
-
insert(index, x) : 将 数据 x 添加到 指定索引 index 的前面 ,
如果 index 超出了 正索引的 范围 ,则 添加到列表的尾部 ,
如果 index 超出了 负索引的 范围, 则添加到 列表的头部
-
python
ls = []
# 向列表中添加数据
ls.append(10)
ls.append(20)
# 向 指定索引位置前 添加数据
ls.insert(0, 5)
# 如果添加的索引值 超出了 正索引的范围
ls.insert(10, 1000)
# 向索引为 -1 的位置前 添加一个元素 xyz
ls.insert(-1, "xyz")
# 如果添加的索引值 超出了 负索引的范围 、则添加到头部
ls.insert(-100, "abc")
-
修改数据
pythonls[index] = valindex 的索引值 不能超出 列表的索引范围 、否则会产生 IndexError 错误!
-
删除数据
- pop(index) : 根据 索引 删除指定位置的元素、 如果 没有传入 index 值, 则默认删除 列表中的最后一个数据
- remove(val) : 根据值删除 列表中满足条件的 第一个元素 ,如果元素不存在,则产生 ValueError 错误
-
查看数据
- lsindex : 获取指定索引位置的元素,索引如果超出范围,会产生错误~~~
- ls.index(val) : 根据 指定的值 获取该值 在列表中 第一次出现的索引位置 ,如果不存在, 则产生 ValueError错误
- ls.index(val, start, end) : 从指定区间 [start, end) 查找第一个 val 元素的索引位置,找不到抛出错误!
列表中常见的方法
- clear() : 清空列表
- copy() : 浅克隆/拷贝一个列表
- count(val) : 获取 val 在列表中出现的个数
- extend(iterable) : 将一个 可迭代对象中的数据 追加到 列表的尾部
- reverse() : 将列表中的元素 进行反转
- sort(key=fn, reverse=False) : 对列表中的元素 进行排序 , key 用来设置排序规则, reverse 用来反转 排序规则,默认按照 自然顺序
列表的遍历方式
-
基于 索引的遍历方式
pythonls = [32, 65 ,23 ,76 , 43] # 基于索引的遍历方式 for i in range(len(ls)): print(i, ls[i]) -
基于 值的遍历方式
pythonfor val in ls: print(val) -
基于 索引和 值的遍历方式
pythonfor index, val in enumerate(ls): print(index, val)
列表支持的运算符
-
列表支持
+和*运算 -
+: 两个列表支持 使用 加法运算 进行 数据合并 ~~ -
*: 一个列表 和一个整数 ( >=0 ) 支持 乘法运算 、将列表中的元素重复 多少次 -
列表支持 关系运算符 >, >= , < , <= , == , !=
两个列表 同位置的元素 进行比较、且 同位置的元素 类型必须 可以支持比较大小
-
列表支持 成员运算符 in , not in
列表的切片技术
切片是用来 快速 提取 列表中 数据的 一种 手段 、
切片 主要通过
[start: end: step]语法来实现的start : 从 哪一个索引位置 开始进行剪切,
当 步长 > 0 的时候, start 默认值是 0, 当步长 <0 , 默认值是 -1
end : 在 哪一个索引位置 结束剪切 (不包含结束索引位置)
当 步长 > 0 的时候, end 的默认值是 列表的长度、当步长 < 0, 默认值是 -length - 1
step : 默认值 是1, 代表 每次 剪切数据的步长
0 : 从左 向右 进行 剪切
< 0 : 从 右 向左 进行 剪切
-
提取数据
pythonls = [1, 34, 65, 23, 76, 23, 87, 3, 87] # 克隆列表 print(ls[:]) # 剪切列表中的最后2个数据 print(ls[-2:]) # 从列表起始索引位置剪切到 列表最后一位、且不包含最后一个数据 print(ls[:-1]) # 去掉列表 前面 2个数据 和 后面 1个数据 print(ls[2:-2]) # 从 右向左剪切,没有区间 所以返回 空列表 print(ls[2:-2:-1]) # 从右向左剪切,去掉 最后一个数据 和 前三个数据 print(ls[-2:2:-2]) # 将列表中的元素 进行反转 print(ls[::-1]) -
修改数据
当切片进行数据修改的时候、步长为1的时候,那么修改提供的值的个数 可以任意 , 如果 步长不为 1, 那么 修改的数据个数 必须和 切片提取到的数据个数 保持完全一致 ~~~
-
删除数据
pythonls = [23, 65, 12, 65, 23, 76, 43 ,78] del ls[2:-2:2]
列表排序
了解常见的排序算法 、冒泡排序、选择排序 、插入排序、 、快排 、堆排序、 桶排序、 归并排序 等 。。。。
不太正规的排序算法(好玩的排序算法): 随机排序 、睡眠排序
- 冒泡排序
每两个相邻的数 进行 比较 、如果大值在前、小值在后、则进行交换~, 每一轮比较 可以实现 将列表中的最大值 沉到 尾部 ~~~~
冒泡排序的时间复杂度是 O(n^2)
python
ls = [32, 65, 12, 65, 12, 67, 1, 34, 87]
# 获取列表长度
length = len(ls)
# 外层循环 负责控制 需要比较多少 轮
for x in range(0, length - 1):
# 内层循环 负责 两个相邻的数字 进行比较
for y in range(length - x - 1):
# 获取 2个相邻的数字
if ls[y] > ls[y + 1]:
# 交换
ls[y], ls[y+1] = ls[y+1], ls[y]
# 整个循环结束后、输出已排序后的列表
print(ls)
- 选择排序
将 列表中的 一个元素 和 它 后面的所有元素 进行比较,如果大值在前,小值在后,进行交换,每一轮可以将列表中最小值沉到列表头部
选择排序的时间复杂度是 O(n^2)
python
ls = [10, 6, 32, 76, 43, 7, 12]
# 获取长度
length = len(ls)
# 编写一个循环 、用来控制要比较的数字
for x in range(0, length - 1):
# 编写一个循环、用来获取 当前 x 位置后的所有数据
for y in range(x+1, length):
# 比较 这个数 和它后面的数大小
if ls[x] > ls[y]:
# 交换
ls[x], ls[y] = ls[y], ls[x]
# 整个循环结束后、输出已排序后的列表
print(ls)
- 插入排序
将列表中的一个元素 插入 到 以一个已经排序好的列表中
插入排序算法 是对 选择排序的 优化, 时间复杂度是 O(n) ~ O(n^2)
二维列表
如果一个列表 、它的所有元素 仍旧是 列表、那么这个列表 被称为 二维列表 。
元组 tuple
是用来进行多个数据存储的容器、用法上 和 列表非常相似 ~
元组 和 列表的区别
- 元组是不可变的、 列表是可变的
- 元组 使用 小括号 () 进行标记 、列表 使用 中括号 \[\] 进行标记
- 元组性能 比 列表更高 、内存占用更小
元组的创建方式
-
字面量定义方式
a) 元组中 有且只有 1个数据的时候, 这个数据的后面 必须添加 逗号
b) 当创建一个 非空元组的时候,小括号可以省略不写 !!!(不推荐)
c) 元组中的元素类型 支持 存储 可变类型 、但不推荐~~~
python
# 创建一个 空的元组,
tp = ()
tp2 = ()
# 输出 2个元组的内容是否相同
print(tp == tp2)
# 比较 2个元组的地址是否相等
print(tp is tp2)
# 创建一个长度 为 1的元组
tp3 = 100,
tp4 = (100, )
print(tp3 is tp4)
# 创建一个 长度 > 1 的元组
tp5 = (1, 2, 3, 4, 5)
print(tp5, type(tp5))
tp6 = ([1, 2, 3], [4, 5, 6])
tp7 = ([1, 2, 3], [4, 5, 6])
print(tp6 is tp7)
# 元组中 只有存储的有可变类型,地址则不同
ls1 = [1, 2, 3]
ls2 = [4, 5, 6]
tp8 = (ls1, ls2)
tp9 = (ls1, ls2)
print(tp8 is tp9)
- tuple 工厂函数 进行创建 tuple 函数 可以将任意的
可迭代对象转成 元组
元组的基本操作
- 使用
[index]语法 可以获取数据 - index(val) : 根据值找满足条件的一个元素的索引,如果找不到 ,则产生 ValueError 错误
- index(val, start, end) : 从指定区间 [start, end) 查找第一个 val 元素的索引位置,找不到抛出错误!
- count(val) : 统计 val 在元组中出现的次数
元组的遍历方式
- 基于索引的遍历方式
- 基于值得遍历方式
- 基于 索引和值的遍历方式
元组支持的运算符
- 算术运算符: 具体用法参考列表
- 关系运算符 : >, >= , < , <= , == , !=
- 成员运算符: in , not in
元组切片
- 提取数据
集合 set
集合是一个用来存储多个数据的容器、底层采用 散列表 + 链表的数据结构 实现的 ~ 、集合存储的数据是
无序且不可重复。散列表 : 也叫 哈希表 、是一种 基于 hash 算法 进行数据存储的 表 , 在 python 语言中 可以通过 hash 函数 获取 某个数据的 hash值
集合中 存储的数据 必须是 可哈希 (能够使用hash算法进行计算) 的
hash 算法的特点是: 相同内容的 hash值一定相同, 内容不同 hash 值 尽可能地不同 ~~
可变 类型 不可做 hash 运算 ~~~
集合中存储的数据是 不可变的、 集合是可变的。
集合去重的原理
存储一个新的数据的时候,会先 获取该数据的 hash值、再将 hash值 和 散列表的 长度 - 1 做 位与运算,得到该数据要存储的索引位置
根据索引位置 判断 该位置 是否已有数据 、 如果 没有数据、 则直接将数据 存入(链表)即可 , 如果该位置有数据、则 遍历该位置的链表组成的数据 , 判断 链表中 每一个数据的 hash 值是否 和 插入数据的 hash 值一致, 如果没有一致的, 则插入到 链表的尾部
如果 找到 hash 相同的值 , 则 比较 2个值的 内容 是否相等, 如果不相同, 插入到链表尾部, 否则 什么都不做
当 hash 值相同 、且 内容相同 则 去重
集合的创建方式
-
字面量定义方式
-
set 工厂函数创建
-
集合生成推导式
python# 创建一个 包含指定数据的集合 set1 = {1, 2, 3, 4, 5, 1, 2, 3} # 不能存储 可变类型, 可变类型不可hash # set1 = { [1, 2, 3], 1, 2, 3 } # set1 = { {1, 2, 3}, {4, 5, 6} } # 创建一个 空的集合 , 空集合 只能使用 set() 来构建 set1 = set() # set 工厂函数 可以将 任意 可迭代对象 转成 集合 string = "hello3" set1 = set(string) print(set1, type(set1)) # 使用 集合生成推导式 快速构建集合 set1 = { x for x in range(1, 10)} print(set1)
集合的基本操作
-
add(x) : 添加数据
pythonset1 = set() # 向集合中添加一个元素 set1.add(10) set1.add("xyz") set1.add("hello") set1.add(True) # 添加一个重复数据、会被自动去掉 set1.add(True) print(set1) -
pop() : 随机从集合中删除一个元素
-
remove(val) : 删除集合中 指定的元素、如果该元素 不存在,则 抛出 KeyError 错误
-
discard(val) : 删除集合中 指定的元素、 无论元素是否存在,都不会报错
集合中常见的方法
- clear() : 清空集合
- union(set) : 计算 2个集合的并集 、并返回新的集合
- update(set) : 计算 2个集合的并集 、将结果 存储到第一个集合中
- intersection(set) : 计算 2个集合的交集 、并返回新的集合
- intersection_update(set) : 计算 2个集合的交集 、将结果 存储到第一个集合中
- difference(set) : 计算 2个集合的差集 、并返回新的集合
- difference_update(set) : 计算 2个集合的差集 、将结果 存储到第一个集合中
- symmetric_difference(set) : 计算 2个集合的补集 、并返回新的集合
- symmetric_difference_update(set) : 计算 2个集合的补集 、将结果 存储到第一个集合中
- issubset(set) : 判断 set 是否是 指定集合的 子集
- issuperset(set) : 判断 set 是否是 指定集合的 超集
集合常见的运算符
- 算术运算符 :
-: 两个集合 可以使用 减号 做 差集运算 - 位运算符 : & (交集) 、 | (并集) 、 ^ (补集)
- 成员运算符: in , not in
- 关系运算符 : == (比较 2个集合是否相等) 、 != 、 > , >= (超集) , < (真子集) , <= (子集)
字典 dict
字典是用来 存储 键值对结构的数据容器 、每一个数据 都包含 键和值, 键和值之间使用
:分割、多个数据之间使用,分割。字典中的键 是 无序且 不可重复的 、键的类型 不能是 可变类型 。值的类型 没有限制
是可变数据类型
字典的创建方式
-
使用字面量的方式定义
python# 创建一个空的字典 dct = {} # 创建一个带有内容的字典 dct = {"name": "张三" , "age": 20, } -
使用 dict 工厂函数定义
python# 创建一个空的字典 dct = dict() # 将一个形似字典的可迭代对象(可迭代对象中的每一个数据 也是一个可迭代对象,且长度为 2 ) 转成字典 mapping = [ ("name", "张三") , ("age", 20) , ("gender", "男")] dct = dict(mapping) # 将 关键字参数转换成字典 dct = dict(a=1, b=2, c=3, abc=4) -
字典生成推导式
pythonls = [1, 2, 3, 4, 5] dct = { x:None for x in ls} dct = { index:x for index, x in enumerate(ls) }
字典的基本操作
-
添加数据
pythondct = {} # 键如果不存在,则添加数据、否则 修改数据 dct["name"] = "张三" # 向字典中 添加一个键值对数据、如果键不存在,则添加, 否则 什么都不做 ~~~ dct.setdefault("name", "李四") -
修改数据
python# 键如果不存在,则添加数据、否则 修改数据 dct["name"] = "张三" -
删除数据
python# 根据 键删除、并返回对应的值、 如果键不存在,则抛出 KeyError错误 dct.pop("name") # 根据 键删除、并返回对应的值、 如果键不存在,则返回 默认值 dct.pop("name", None) del dct["name"] -
获取数据
python# 根据键获取值、如果 键不存在,则抛出 KeyError 错误 name = dct["name"] # 根据键获取值、如果 键不存在 , 则返回 None name = dct.get("name") # 根据键获取值、如果 键不存在 , 则返回 张三 name = dct.get("name", "张三")
字典中常见的方法
- copy() : 浅克隆一个新字典
- clear() : 清空字典
- update(dict) : 合并 2个字典
- items() : 获取 所有键值对组成的 可迭代对象 ,格式 ("name" , "张三"), ("age" , 20)
- keys() : 获取 字典中所有 键 组成的 可迭代对象, 格式 "name" , "age"
- values() : 获取 字典中 所有 值组成的可迭代对象
字典的遍历方式
-
键遍历
pythondct = {"name": "张三", "age": 20, "gender": "男"} # 键遍历 for key in dct: print(key, dct.get(key)) -
值遍历
pythondct = {"name": "张三", "age": 20, "gender": "男"} # 值遍历 for val in dct.values(): print(val) -
键值对遍历
pythondct = {"name": "张三", "age": 20, "gender": "男"} # 键值对遍历 for key, val in dct.items(): print(key, val)
字典支持的运算符
- 位运算符:
|合并 2个字典 、并返回一个合并后的新字典 - 成员运算符 : in , not in : 判断 字典中的键是否存在
- 关系运算符 : 只 支持
==和!=两个运算