第1节 列表
1 内置容器与序列
在Python中,我们经常需要处理和组织多个数据,内置容器就为我们提供了方便的方式,来去存储和管理数据的集合。
- 列表
- 字符串
- 元组
- 集合
- 字典
序列,是一种特殊类型的内置容器,强调元素的有序性(不是说排序,元素的出入顺序是可以确定的)和可迭代性(可以通过角标、切片、循环的方式来访问数据元素),常见的序列类型包括:
- 列表
- 元组
- 字符串
- 字节序列
- 范围对象(Range)
2 列表的定义与创建
列表是一种有序的可变序列(容器中的元素是可以改变的),它可以容纳任意类型的元素(整数、小数、字符串、布尔、列表、字典、元组…都可以),当然为了方便统一管理和存储,一般都建议存储的是同类型的元素。
为什么会存在列表?就是因为不想用多个变量存储多个数据,这样子的话不方便操作。
a = 1
b = 2
c = 1
d = 3
arr = [1,2,1,3]

所谓的列表,就是堆内存中,地址连续且大小相等的一组连续的存储空间
列表中,元组之间用逗号分隔,整个列表用方括号[]
括起来的。
# 创建空列表
arr = [] # []
# 通过list()内置函数创建空列表
arr = list() # []
# 利用list()内置函数将一个字符串转换为列表
arr = list("abc") # ['a', 'b', 'c']
# 利用list()内置函数将range()转换为列表
arr = list(range(2,11,2)) # [2, 4, 6, 8, 10]
arr = [1, 2, 3, 4, 5] # [1, 2, 3, 4, 5]
# 不推荐这么进行赋值 一般建议同类型
arr = [3.14, "Hello", True, 2 + 3j, print]
# 创建长度为10, 默认值为0的列表
arr = [0] * 10 # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# 列表推导式 就是通过循环来创建一个列表
arr = [i for i in range(1, 11)] # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr = [i ** 2 for i in range(1, 11)] # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
arr = [i for i in range(1, 101) if i % 3 == 0 and i % 5 == 0] # [15, 30, 45, 60, 75, 90]
# 可以将其他的列表转为当前的列表
lst = [1, 2, 3, 4]
arr = [i + 1 for i in lst] # [2, 3, 4, 5]
# 集合input map使用
# 输入 1 2 3
arr = input("输入一组数字,用空格隔开:").split(" ")
# ['1', '2', '3']
arr = list(map(int, input("输入一组数字,用空格隔开:").split(" ")))
# [1, 2, 3]
arr = [int(i) for i in input("输入一组数字,用空格隔开:").split(" ")]
# [1, 2, 3]
print(arr)
3 序列的通用操作
适用于列表、元组、字符串
(1)索引
就是元素在序列中的位置,位置默认都是从0开始的,可以通过索引来访问任何合法位置的元素
arr = [1, 2, 3, 4, 5]
序列除了支持正数角标外,也支持负数角标,负数角标意义为倒数第几个

arr = [1, 2, 3, 4, 5]
print(arr[0])
print(arr[4])
print(arr[-1])
print(arr[-3])
# print(arr[5]) # IndexError: list index out of range 角标越界
# print(arr[-6]) # IndexError: list index out of range
(2)切片
就是通过一种快捷的方式,来获取序列中一个连续的子区间
sequence[start:stop:step]
,和之间的range很像
- start 指的是起始角标 不写的话 默认从角标0开始
- stop 指的是结束角标 不取 [start, stop),不写的话,默认一直到最后一个元素(包含)
- step 指的是步长,不写的话默认为1
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 从角标2开始,直到表尾 步长默认为1
print(arr[2:]) # [3, 4, 5, 6, 7, 8, 9]
# 从角标2开始,直到表尾 步长为2
print(arr[2::2]) # [3, 5, 7, 9]
# 从角标0开始,直到角标6(不包含) 步长1
print(arr[:6]) # [1, 2, 3, 4, 5, 6]
# 从角标2开始,直到角标8(不包含) 步长3
print(arr[2:8:3]) # [3, 6]
# 从头到尾 步长2
print(arr[::2]) # [1, 3, 5, 7, 9]
# 逆序
print(arr[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1]
# 从角标2到倒数第2个(不包含),步长2
print(arr[2:-2:2]) #[3, 5, 7]
# 从倒数第2个到角标2 步长2
print(arr[-2:2:2]) # []
# 从倒数第2个到角标2 步长-2
print(arr[-2:2:-2]) # [8, 6, 4]
正步长 step > 0 start在前,stop在后 start -> stop 从左到右
负步长 step < 0 start在后,stop在前 start -> stop 从右到左
(3)连接与重复
可以使用+
做序列的拼接,可以使用*
做序列的重复
arr1 = [1, 2, 3]
arr2 = [4, 5, 6]
arr3 = arr1 + arr2
print(arr1) #[1, 2, 3]
print(arr2) #[4, 5, 6]
print(arr3) #[1, 2, 3, 4, 5, 6]
arr4 = arr3 * 3
print(arr3) #[1, 2, 3, 4, 5, 6]
print(arr4) #[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
(4)长度与最值
就是关于列表的一些内置函数,重点对序列进行操作
arr = [1, 3, 2]
print(len(arr)) # 3 列表中的元素个数
print(max(arr)) # 3 列表中的最大值
print(min(arr)) # 1 列表中的最小值
print(sum(arr)) # 6 列表中的元素和
max = 10
print(max(arr)) # TypeError: 'int' object is not callable
(5)成员资格检查
使用 in 和 not in 来判断一个元素是否存在与序列当中,计算结果为布尔类型
arr = [1, 3, 2]
print(4 in arr)
print(4 not in arr)
s = "Hello World Python"
print("Python" in s)
print("X" not in s)
4 列表对象函数
对象函数是区别于内置函数和自定义函数的,内置函数我们可以直接调用,自定义函数先定义后调用,对象函数必须通过具体的对象来进行调用 对象.函数()
,说白这些函数可以认为是某一个数据对象自身携带的功能。具体看一下列表中的对象函数都有哪些:
>>> help()
help> list # 查看list的详细介绍
append(object):在列表末尾添加元素object
arr = [1, 2, 3]
arr.append(4)
print(arr)
arr.append(5)
print(arr)
insert(index,object):在指定角标index处,插入元素object
arr = [1, 2, 3]
"""
1 2 3
0 1 2
-3 -2 -1
"""
arr.insert(0, 4)
print(arr) # [4, 1, 2, 3]
arr.insert(3, 5)
print(arr) # [4, 1, 2, 5, 3]
arr[0] = 6
print(arr) # [6, 1, 2, 5, 3]
arr.insert(5, 4) # 在表尾添加
print(arr) # [6, 1, 2, 5, 3, 4]
arr.insert(len(arr), 7) # 在表尾添加
print(arr) # [6, 1, 2, 5, 3, 4, 7]
# 当index > len(arr) 看做表尾添加
arr.insert(1000, 8) # [6, 1, 2, 5, 3, 4, 7, 8]
print(arr)
# 当index < -len(arr) 看做表头添加
arr.insert(-10, 9) # [9, 6, 1, 2, 5, 3, 4, 7, 8]
print(arr)
arr.insert(4, True)
print(arr) # [9, 6, 1, 2, True, 5, 3, 4, 7, 8]
extend(sequence):将序列sequence中的元素逐个添加在表尾
arr1 = [1, 2, 3]
arr2 = [4, 5, 6]
arr1.append(arr2)
print(arr1) # [1, 2, 3, [4, 5, 6]]
print(len(arr1))
arr1 = [1, 2, 3]
arr2 = [4, 5, 6]
arr1.extend(arr2)
print(arr1) # [1, 2, 3, 4, 5, 6]
remove(value):从左到右删除列表中第一次出现的元素value,没返回值,找不到会报错
arr = [3,2,1,3,2,1]
arr.remove(3)
print(arr) # [2, 1, 3, 2, 1]
# 如果删除一个不存在的元素 则报错
arr.remove(6) # ValueError: list.remove(x): x not in list
print(arr)
pop(index):删除指定角标index处的元素,并将其返回,如果角标不在合法范围内,则报错
arr = [1,2,3,4]
# 删除表尾 -> 出栈
# append 在表尾添加 -> 入栈
print(arr.pop()) # 4
print(arr) # [1, 2, 3]
print(arr.pop(0)) # 1
print(arr) # [2, 3]
print(arr.pop(5)) # IndexError: pop index out of range
print(arr)
额外补充:
对于栈的操作 进栈push 出栈pop 查看栈顶 peek
对于队列的操作 入队pull 出队poll 查看队首 element
del 关键字:不是列表对象函数,删除元素但不会返回其值
arr = [1,2,3,4]
# 通过角标删除一个元素
del arr[0]
print(arr) # [2,3,4]
# del arr[-8] # IndexError: list assignment index out of range
arr = [1,2,3,4,5,6,7,8,9]
# 通过切片删除一组元素
del arr[::2]
print(arr) # [2, 4, 6, 8]
sort():排序
arr = [9,2,4,8,3,1,6,5,7]
arr.sort()
print(arr)
arr = [9,2,4,8,3,1,6,5,7]
arr.sort(reverse=True)
print(arr)
arr = ["banana", "apple","cat","dog","elephant","fruit","girlfreind"]
arr.sort()
print(arr)
arr = ["banana", "apple","cat","dog","elephant","fruit","girlfreind"]
# 本身的比较方式是按照元素内容来排序
arr.sort(key=len) # 指定比较的方式 按照序列长度比大小
print(arr)
# Python中 也有关于排序的内置函数 sorted()
arr1 = [9,2,4,8,3,1,6,5,7]
# 以原数组为源 创建新的副本并进行排序 不会对源数据进行改变
arr2 = sorted(arr1)
print(arr1)
print(arr2)
reverse():反转列表
arr = [9,2,4,8,3,1,6,5,7]
arr.reverse()
print(arr)
count(value):统计元素value出现的次数
arr = [1,0,1,0,1,1,1,1,0,1,0,1]
print(arr.count(1))
print(arr.count(0))
print(len(arr))
index(value, start):从指定start默认0处开始从左到右查找元素value第一次出现的位置
arr = [1,0,1,0,1,1,1,1,0,1,0,1]
print(arr.index(0)) # 1
print(arr.index(0,5)) # 8
clear():清空列表 -> []
copy():浅拷贝列表
arr1 = [1,2,3,4,5,6]
arr2 = arr1
arr1[0] = 666
print(arr1)
print(arr2)
arr1 = [1,2,3,4,5,6]
arr2 = arr1.copy() # 创建新的列表对象 和源列表内容一样
arr1[0] = 666
print(arr1) # [666, 2, 3, 4, 5, 6]
print(arr2) # [1, 2, 3, 4, 5, 6]
arr1 = [1, 2, 3, [4, 5, 6]]
arr2 = arr1.copy()
arr1[3][0] = 666
print(arr1) # arr1 = [1, 2, 3, [666, 5, 6]]
print(arr2) # arr2 = [1, 2, 3, [666, 5, 6]]
第2节 列表相关操作
无论是内置函数、对象函数,用起来确实很方便,但是作为初学者,你必须懂得它们背后的运行逻辑!
1 常规操作
(1)遍历
arr = [1,2,3,4]
# 以索引遍历:可以在遍历期间修改元素
for index in range(len(arr)):
arr[index] = arr[index] ** 2
print(arr[index])
print(arr)
# 以元素遍历:只能获取元素,不能修改元素值(可变对象除外)
for element in arr:
print(element)
# 同时遍历角标和元素
for index, element in enumerate(arr):
print(f"角标{index},元素{element}")
# 反向遍历
for index in range(len(arr) - 1, -1, -1):
print(arr[index])
(2)最值
def min_max(arr:list[int]) -> tuple:
min_value = arr[0]
max_value = arr[0]
for i in range(1, len(arr)):
if arr[i] > max_value:
max_value = arr[i]
if arr[i] < min_value:
min_value = arr[i]
return min_value, max_value
arr = [2,9,8,1,7,4,6,3,5]
min_val, max_val = min_max(arr)
print(min_val, max_val)
(3)存在性
arr = [2,9,8,1,7,4,6,3,5]
#O(n)
def is_exist(arr:list[int], key:int) -> bool:
for element in arr:
if element == key:
return True
return False
print(is_exist(arr, 10))
print(is_exist(arr, 8))
(4)反转
arr = [1,2,3,4,5,6,7,8,9]
"""
1 2 3 4 5 6 7 8 9
l
r
"""
def list_reverse(arr:list[int]) -> None:
l = 0
r = len(arr) - 1
while l < r:
arr[l], arr[r] = arr[r], arr[l]
l += 1
r -= 1
list_reverse(arr)
print(arr)
(5)乱序
import random
arr = [1,2,3,4,5,6,7,8,9]
def list_shuffle(arr):
for i in range(len(arr)):
j = random.randint(0, len(arr) - 1)
arr[i], arr[j] = arr[j], arr[i]
list_shuffle(arr)
print(arr)
(6)二维列表
所谓的二维列表,其实本质上就是一个一维列表,只不过该一维列表中的每一个元素为其他的一维列表
def print_matrix(matrix):
for i in range(len(matrix)):
for j in range(len(matrix[i])):
print(matrix[i][j], end = ' ')
print()
# 直接填值创建二维列表
matrix = [[1,2,3], [4,5,6], [7,8,9]]
print(len(matrix))
print(len(matrix[1]))
print_matrix(matrix)
matrix = [
[1,2,3,4],
[1,2,3],
[1,2],
[1]
]
print_matrix(matrix)
# 循环创建二维列表 指定默认值 0
rows = 3
cols = 5
matrix = []
for i in range(rows):
row = [0] * cols
matrix.append(row)
matrix[2][2] = 6
print_matrix(matrix)
# 列表推导式创建二维列表
matrix = [[0] * cols for _ in range(rows)]
matrix[2][2] = 6
print_matrix(matrix)
matrix = [ [i + j for j in range(cols)] for i in range(rows)]
print_matrix(matrix)
2 查找操作
(1)二分查找
前提数据必须是有序的(升序、降序)
# 返回的是元素key在arr中的角标 如果不存在则返回-1
def binary_search(arr, key): #O(log n)
left = 0
right = len(arr) - 1
mid = (left + right) // 2
while arr[mid] != key:
if arr[mid] < key:
left = mid + 1
elif key < arr[mid]:
right = mid - 1
if left > right:
return -1
# 重新更新mid的值
mid = (left + right) // 2
return mid
# 顺序查找
def linear_search(arr, key):
for index in range(len(arr)):
if arr[index] == key:
return index
return -1
# arr = [1,2,3,4,5,6,7,8,9]
# key = 6
# print(binary_search(arr, key))
"""
n/2/2/2/2/..../2 = 1
n/2^x = 1
n = 2^x
x = logn
"""
arr = []
for i in range(70000000):
arr.append(i)
key = 69999999
print("数据创建完毕...")
print(binary_search(arr, key))
print(linear_search(arr, key))
(2)插值查找
前提数据必须是有序的(升序、降序),它是二分查找的升级版本
# mid = (key - arr[left]) / (arr[right] - arr[left]) * (right - left) + left
# 当数据分布比较均匀的时候 大致满足等差序列的情况 性能要比二分查找要优秀
def interpalotion_search(arr, key):
count = 0
left = 0
right = len(arr) - 1
mid = 0
# mid = int((key - arr[left]) / (arr[right] - arr[left]) * (right - left)) + left
# while arr[mid] != key:
# count += 1
# if arr[mid] < key:
# left = mid + 1
# elif key < arr[mid]:
# right = mid - 1
# if left > right:
# mid = -1
# break
# mid = int((key - arr[left]) / (arr[right] - arr[left]) * (right - left)) + left
while True:
count += 1
mid = int((key - arr[left]) / (arr[right] - arr[left]) * (right - left)) + left
# key本身在范围外 没找到
if mid < left or mid > right:
mid = -1
break
if arr[mid] < key:
left = mid + 1
elif key < arr[mid]:
right = mid - 1
else:
break
# 在范围内没找到
if left > right:
mid = -1
break
print(f"插值查找count={count}")
return mid
def binary_search(arr, key): #O(log n)
count = 0
left = 0
right = len(arr) - 1
mid = (left + right) // 2
while arr[mid] != key:
count += 1
if arr[mid] < key:
left = mid + 1
elif key < arr[mid]:
right = mid - 1
if left > right:
mid = -1
break
# 重新更新mid的值
mid = (left + right) // 2
print(f"二分查找count={count}")
return mid
# int((20 - 1)/(20-1) * (19 - 0)) + 0 = 19
# int((100 - 1)/(20 - 1) * (19 - 0))+ 0 = 99
# int((-100 - 1)/(20 - 1)*(19-0)) + 0 = - 101
arr= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
key = -100
print(binary_search(arr, key))
print(interpalotion_search(arr, key))
3 排序操作
希尔排序、堆排序、快速排序、归并排序、计数排序、基数排序、桶排序
(1)选择排序
# 选择排序 O(n^2)
"""
从前往后 每一个元素都要跟其后面的其他元素作比较
如果出现左大右小 则进行交换
"""
def selection_sort(arr):
for i in range(len(arr) - 1): # 少一轮
for j in range(i + 1, len(arr)):
if arr[i] > arr[j]:
arr[i], arr[j] = arr[j], arr[i]
arr = [5,2,3,1,4]
selection_sort(arr)
print(arr)
(2)冒泡排序
# 冒泡 O(n^2)
"""
从前往后 元素之间两两进行比较
如果左大右小则交换
"""
def bubble_sort(arr):
# 0 1 2 3
for i in range(len(arr) - 1): #-1 少一轮
for j in range(len(arr) - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
arr = [5,2,3,1,4]
bubble_sort(arr)
print(arr)
(3)插入排序
# 选择 O(n^2)
def insertion_sort(arr):
# 从第2个元素开始遍历
for i in range(1, len(arr)):
j = i
while j > 0 and arr[j - 1] > arr[j]:
arr[j - 1], arr[j] = arr[j], arr[j - 1]
j -= 1
arr = [5,2,3,1,4]
insertion_sort(arr)
print(arr)
根据循环的特性来去解决特定的问题,而不是学习循环本身,学算法思想!
循环之间的好坏其实也有区别,主要在于数据的分布情况
(1)大致升序
(2)大致降序
(3)趋于稳定(方差小,相等值比较多)
(4)完全随机
用time模块记录一下运行时间