文章目录
- 时间复杂度
- 空间复杂度
- 递归
- 汉诺塔问题(递归实例)
- 查找
- 列表查找
- 顺序查找(Linear Search)
- 二分查找
- 排序
- 列表排序
- 冒泡排序(Bubble Sort)
- 选择排序
- 插入排序
- 快速排序
- 堆排序
- 归并排序
- 希尔排序
- 计数排序
- 基数排序
时间复杂度
-
时间复杂度是用来估计算法运行时间的一个式子(单位)
-
一般来说,时间复杂度高的算法比时间复杂度低的算法慢
-
常见的时间复杂度(按效率排序):
- O(1) < O(logn) <O(n) < O(nlogn) < O(n^2) < O(n ^2logn) < O(n^3)
-
O(1)和O(n)和O(n^2),评估一个算法运行时间的单位
-
基础计算在计算机里面,只要没上升到n的级别时间复杂度都是O(1)
-
O(1): 可以理解为1s,简单计算都在1s内完成
# O(1)
print('hello world')
# O(n)
for i in range(n):
print('hello world')
# O(n^2)
for i in range(n):
for j in range(n):
print('hello world')
# O(n^3)
for i in range(n):
for j in range(n):
for k in range(n):
print('hello world')
# O(3) ? 错,是O(1),因为O(1)理解为是单位
print('hello world')
print('hello world')
print('hello world')
# 假设n = 64
# O(log2n) 或 O(logn) 因为,2^6 = 64
while n > 1:
print(n)
n = n // 2
- 如何快速判断算法复杂度?(简单算法)
- 确定问题规模n
- 循环减半过程 logn
- k层关于n的循环 (n的几次方) n^k
空间复杂度
- 用来评估算法内存占用大小的式子
- 空间复杂度的表示方式与时间复杂度完全一样
- 算法使用了几个变量: O(1)
- 算法使用长度为n的一维列表:O(n)
- 算法使用了m行n列的二维列表: O(mn)
- 空间换时间
递归
- 递归的两个特点
- 调用自身
- 结束条件
汉诺塔问题(递归实例)
- n = 2 的时候步骤:
- 小盘子从a到b
- 大盘子从a到c
- 小盘子从b到c
- n个的步骤:
- n-1 从a经过c到b
- n从a到c
- n-1从b经过a到c
# 按照上面的逻辑实现
def hanoi(n, a, b, c):
if n > 0:
hanoi(n-1, a, c, b)
print('moving from %s to %s' % (a, c))
hanoi(n-1, b, a, c)
hanoi(3, 'A', 'B', 'C')
查找
- 什么是查找?在一些元素中,通过一定方法找出与给定关键字相同的数据元素的过程
列表查找
- 什么是列表查找?从列表中查找指定元素
- 输入:列表,待查找元素
- 输出: 元素下标(未找到元素时一般返回None或-1)
- 内置列表查找函数:index()
顺序查找(Linear Search)
- 也叫线性查找,从列表第一个元素开始,顺序进行搜索,知道找到元素或者搜索到列表的最后一个元素为止
# 时间复杂度:O(n)
def linear_search(li, val):
for ind, v in enumerate(li):
if v == val:
return ind
else:
return None
print(linear_search([1, 2, 3], 2))
二分查找
- 又叫折半查找,从有序列表的初始化候选区间li[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。
- 前提:有顺序,取中间值,比较大小,目标值在的区域叫做候选区,如何维护候选区?left 和right中间值是mid
# 时间复杂度:O(logn)
def binary_search(li, val):
left = 0
right = len(li) - 1
while left <= right: # 候选区有值
mid = (left + right) // 2
if li[mid] == val:
return mid
elif li[mid] > val: # 待查找的值在mid左侧
right = mid - 1
else: # mid小于val, 待查找的值在mid右侧
left = mid + 1
else:
return None
li = [1, 2, 3, 4, 5]
print(binary_search(li, 3))
排序
- 什么是排序?将一组无序的记录序列调整为有序的记录序列
列表排序
- 将无序列表变为有序列表
- 输入:列表
- 输出:列表
- 升序和降序
- 内置排序函数:sort()
冒泡排序(Bubble Sort)
- 列表每两个相邻的数,如果前面比后面的大,则交换这两个数
- 一趟排序完成后,则无序区较少一个数,有序区增加一个数。
- 代码关键点:趟、无序区范围
import random
# 复杂度o(n^2)
def bubble_sort(li):
for i in range(len(li) - 1): # i表示i趟,且是从0开始的
for j in range(len(li) - i - 1):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1], li[j]
li = [random.randint(0, 10) for i in range(5)]
print(li)
bubble_sort(li)
print(li)
# 复杂度o(n^2), 升级版,就是当一趟不发生任何交换的时候,直接返回结果
def bubble_sort(li):
for i in range(len(li) - 1): # i表示i趟,且是从0开始的
exchange = False
for j in range(len(li) - i - 1):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1], li[j]
exchange = True
print(li)
if not exchange:
return
li = [9, 8, 7, 1, 2, 3, 4, 5, 6]
print(li)
bubble_sort(li)
print(li)
选择排序
- 一趟排序记录最小的数,放到第一位
- 再一趟排序记录记录列表无序区最小的数, 放到第二位
- 。。。。。
算法关键点:有序区和无序区、无序区最小数的位置
# 时间复杂度o(n^2)
def select_sort_simple(li):
li_new = []
for i in range(len(li)):
min_val = min(li)
li_new.append(min_val)
li.remove(min_val)
return li_new
# 时间复杂度o(n^2)
def select_sort(li):
for i in range(len(li) - 1): # 第几趟
min_loc = i
for j in range(i + 1, len(li)): # 无序区长度
if li[j] < li[min_loc]:
min_loc = j
li[i], li[min_loc] = li[min_loc], li[i]
return li
li = [9, 8, 7, 1, 2, 3, 4, 5, 6]
print(select_sort_simple(li))
li = [9, 8, 7, 1, 2, 3, 4, 5, 6]
print(select_sort(li))
插入排序
- 初始手里(有序区)只有一张牌
- 每次从无序区摸出一张牌, 插入到手里已有牌的正确位置
# 时间复杂度O(n^2)
def inster_sort(li):
for i in range(1, len(li)): # 表示摸到的牌的下标
tmp = li[i]
j = i - 1 # j指的是手里的牌的下标
while li[j] > tmp and j >= 0:
li[j + 1] = li[j]
j -= 1
li[j + 1] = tmp
print(li)
li = [3, 2, 44, 1, 5, 7, 9, 6, 8]
print(li)
inster_sort(li)
快速排序
- 思路
- 取一个元素p(第一个元素),使元素p归位
- 列表被p分成两部分,左边都比p小,右边都比p大
- 递归完成排序
- 快速排序的时间复杂度:O(nlogn)
- 快速排序存在的问题
- 最坏情况,数据是由大到小排序的,每次就丢掉一个,时间复杂度是O(n^2)
- 递归:递归耗费比较多的资源
def partition(li, left, right): # 列表, 左边下标,右边下标, 返回中间值的下标
tmp = li[left]
while left < right:
while left < right and li[right] >= tmp: # 从右边找比tmp小的数
right -= 1 # 往左走一位
li[left] = li[right] # 把右边的值填写到左边
print(li, 'right')
while left < right and li[left] <= tmp:
left += 1
li[right] = li[left] # 把左边的值写到右边空位上
print(li, 'left')
li[left] = tmp # 把tmp归位
return left
def quick_sort(li, left, right):
if left < right: # 至少两个元素
mid = partition(li, left, right)
quick_sort(li, left, mid - 1)
quick_sort(li, mid + 1, right)
li = [5, 7, 4, 6, 3, 1, 2, 9, 8]
print(li)
quick_sort(li, 0, len(li) - 1)
print(li)
堆排序
- 知识准备
- 树是一种数据结构。 比如:目录结构
- 树是一种可以递归定义的数据结构