[1 时间装饰器](#1 时间装饰器)
[2 汉诺塔](#2 汉诺塔)
[3 顺序查找和二分查找法](#3 顺序查找和二分查找法)
[4 冒泡排序](#4 冒泡排序)
[5 插入排序](#5 插入排序)
[6 选择排序](#6 选择排序)
1 时间装饰器
python
import time
def cal_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
result = func(*args, **kwargs)
t2 = time.time()
print("%s running time: %s secs." % (func.__name__, t2 - t1))
return result
return wrapper
2 汉诺塔
python
"""
汉诺塔(Tower of Hanoi)是一个经典的数学问题,由法国数学家爱德华·卢卡斯(Édouard Lucas)在1883年提出。
它由三个柱子和若干个直径不同的圆盘组成。起初,所有的圆盘都按直径从大到小堆叠在第一个柱子上。
问题的目标是通过一些步骤将整个圆盘堆移动到另一个柱子上,且在移动过程中需要遵守以下规则:
1. 每次只能移动一个圆盘。
2. 每次移动的圆盘必须放在另一个柱子上,并且放置在那个柱子上已经存在的圆盘之上(如果有的话),且必须小于下方的圆盘。
这个问题的核心是找到最少的步骤将所有的圆盘移动到目标柱子上。对于n个圆盘,最少的移动次数是 \(2^n - 1\)。
### 例子
假设有3个圆盘,初始状态如下:
- 圆盘1(最小)在最上面,圆盘3(最大)在最下面,都在柱子A上。
目标是将这3个圆盘按照同样的顺序移动到柱子C上。
最少的移动步骤为:
1. 将圆盘1从柱子A移动到柱子C。
2. 将圆盘2从柱子A移动到柱子B。
3. 将圆盘1从柱子C移动到柱子B(放在圆盘2上)。
4. 将圆盘3从柱子A移动到柱子C。
5. 将圆盘1从柱子B移动到柱子A。
6. 将圆盘2从柱子B移动到柱子C(放在圆盘3上)。
7. 将圆盘1从柱子A移动到柱子C(放在圆盘2上)。
### 数学表达式
汉诺塔问题的最优解可以用递归算法来表示,其中:
- \( T(n) \) 表示移动n个圆盘所需的最少步数。
- 对于n个圆盘,递归公式为 \( T(n) = 2T(n-1) + 1 \),其中 \( T(1) = 1 \)。
汉诺塔问题的时间复杂度是 指数级别的。
具体来说,对于有n个圆盘的汉诺塔问题,最少的移动次数为2的n次方−1。
因此,汉诺塔问题的时间复杂度为O(2的n次方)。
"""
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')
3 顺序查找和二分查找法
python
from cal_time import *
@cal_time
def linear_search(li, val): # 线性排序
for ind, v in enumerate(li):
if v == val:
return ind
else:
return None
@cal_time
def binary_search(li: list, val: int):
"""
二分查找法(Binary Search)是一种高效的查找算法,适用于在一个已经排序的数组或列表中查找某个特定的元素。
二分查找的核心思想是将搜索范围不断折半,从而迅速缩小查找范围。
工作原理
二分查找通过以下步骤来查找目标值:
初始化:设定两个指针,分别指向数组的起始位置和结束位置,通常称为 left 和 right。
查找中间元素:
计算中间位置 mid,mid 的索引通常通过公式 mid = (left + right) // 2 计算。
将目标值与 mid 位置的元素进行比较。
缩小范围:
如果目标值等于 mid 位置的元素,则查找成功,返回该元素的索引。
如果目标值小于 mid 位置的元素,说明目标值在左半部分,于是将 right 更新为 mid - 1。
如果目标值大于 mid 位置的元素,说明目标值在右半部分,于是将 left 更新为 mid + 1。
重复:
重复以上步骤,直到找到目标值或搜索范围为空(即 left > right)。
返回结果:
如果找到目标值,返回其索引。
如果找不到,通常返回 -1 或其他特定值以表示查找失败。
时间复杂度
二分查找的时间复杂度为O(logn),其中 n 是数组中的元素数量。
相比于线性查找O(n),二分查找在处理大规模数据时更加高效。
注意事项
前提条件:二分查找只适用于已排序的数组或列表。
数据类型:二分查找的对象通常是可索引的序列(如数组或列表)。
:param li:
:param val:
:return:
"""
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: # li[mid] < val 带查找的值在mid右侧
left = mid + 1
else:
return None
li = list(range(1000))
# linear_search(li, 389)
binary_search(li, 389)
4 冒泡排序
python
"""
冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复地遍历要排序的列表,
比较相邻的元素并根据需要交换它们的位置,使得每一轮遍历之后,最大的元素"冒泡"到列表的末尾。
这个过程不断重复,直到整个列表有序为止。
算法步骤
从列表的第一个元素开始,依次比较相邻的两个元素。
如果前一个元素大于后一个元素,则交换它们的位置。
对每一对相邻元素执行相同的操作,从列表开始到最后的未排序部分为止。
完成一次遍历后,最大的元素就会移动到列表的末尾。
忽略列表中已排序的部分,重复上述步骤,直到没有元素需要交换,列表排序完成。
时间复杂度
冒泡排序的最坏情况和平均时间复杂度均为
O(n的2次方),其中n 是列表中元素的数量。
虽然这个算法很简单,但由于其效率较低,在实际应用中,通常仅用于教育目的或非常小的数据集
"""
import random
from cal_time import *
@cal_time
def bubble_sort(li: list) -> None:
for i in range(len(li) - 1): # 第i趟
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
if not exchange:
return
li = list(range(10000))
random.shuffle(li) # 用于随机打乱(洗牌)一个列表中的元素顺序
bubble_sort(li)
5 插入排序
python
"""
插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理类似于人们排序扑克牌:
每次将一个新的元素插入到已经排好序的部分中。这个算法对于小规模数据集或者部分已经排序的数据集非常有效。
算法步骤
开始:从第二个元素(索引为1)开始,将其视为待插入的元素。
插入:将该元素与它之前的元素依次比较。如果该元素比前面的元素小,就将前面的元素向后移动一位,
直到找到一个合适的位置将该元素插入。
重复:对每一个未排序的元素重复上述步骤,直到整个数组有序。
时间复杂度
插入排序的时间复杂度为O(n2),其中 n 是数组中的元素数量。在最佳情况下(数组已经有序),时间复杂度为O(n)。
尽管它的效率不如更复杂的排序算法,但由于其简单性和在某些情况下的有效性,插入排序仍然是一个有用的工具。
"""
def insert_sort(li: list):
for i in range(1, len(li)): # i表示摸到的牌的下标
tmp = li[i]
j = i - 1 # j指的就是手里的牌的下标
while j >= 0 and li[j] > tmp:
li[j + 1] = li[j]
j -= 1
li[j + 1] = tmp
print(li)
li = [3, 2, 4, 1, 5, 7, 9, 6, 8]
# print(li)
insert_sort(li)
6 选择排序
python
"""
选择排序(Selection Sort)是一种简单的排序算法,
它的基本思想是每一轮从未排序的部分中选择最小(或最大)的元素,并将其放在已排序部分的末尾。
通过不断地选择和交换位置,最终使整个列表有序。
算法步骤
从未排序的列表中找到最小(或最大)的元素。
将这个元素与未排序部分的第一个元素交换位置,确保这个最小元素成为已排序部分的一部分。
忽略已经排序的部分,继续从剩下的未排序部分中重复上述步骤,直到整个列表排序完成。
选择排序的时间复杂度为O(n2),其中 n 是列表中元素的数量。
这个算法在时间复杂度上和冒泡排序一样,但通常进行的交换次数更少。
因此,虽然它仍然不是最优的排序算法,但在某些特定情况下比冒泡排序更有效。
"""
def select_sort_simple(li: list) -> list:
li_new = []
for i in range(len(li)):
min_val = min(li)
li_new.append(min_val)
li.remove(min_val)
return li_new
def select_sort(li: list):
for i in range(len(li) - 1): # i是第几趟
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]
print(li)
li = [3, 2, 4, 1, 5, 7, 9, 6, 8]
print(li)
select_sort(li)