算法
分类:
IT文章
•
2024-06-27 17:41:31
一.时间复杂度
#在计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。
时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。
#时间复杂度是用来估计算法运行时间的一个式子(单位)
#如何一眼判断时间复杂度?
- 循环减半的过程->O(logn)
- 几次循环就是n的几次方的复杂度
#时间复杂度
- 最优时间复杂度
- 最坏时间复杂度
- 平均时间复杂度
#时间复杂度的几条计算规则
- 基本操作 即只有常数项,认为其时间复杂度为O(1)
- 顺序结构 时间复杂度按加法进行计算
- 循环结构 时间复杂度按乘法进行计算
- 分支结构 时间复杂度取最大值
- 判断一个算法的效率时, 往往只需要关注操作数量的最高次项, 其它次要项和常数项可以忽略
- 在没有特殊说明时,我们分析的算法的时间复杂度都是指最坏时间复杂度
a.测试
def t1():
li = []
for i in range(10000):
li.append(i)
def t2():
li = []
for i in range(10000):
li.insert(0, i)
timer1 = Timer("t1()", "from __main__ import t1")
print("append", timer6.timeit(1000))
timer2 = Timer("t2()", "from __main__ import t2")
print("insert(0)", timer2.timeit(1000))
####################
append 1.1599015080137178
insert(0) 23.26370093098376
append 比 insert 执行效率高
b.二分法
import random
n = 10000
li = list(range(n))
def bin_search(li,val):
low = 0
high = len(li) - 1
while low <= high:
mid = (low + high) // 2
if li[mid] == val:
return mid
elif li[mid] < val:
low = mid + 1
else:
high = mid - 1
return None
obj = bin_search(li,5550)
print(obj)
View Code
c.排序
#排序low B二人组
- 冒泡排序 比较相邻的元素。如果第一个比第二个大,就交换他们两个。依次进行排序。
- 选择排序 一趟遍历记录最小的数,放到第一个位置。再一趟遍历剩余
- 插入排序 摸牌插入,将牌从无序区放到手中有序区,最开始手中有序区只有一张,后面抽牌放入手中有序区。
#排序NB三人组
- 快速排序 取第一个位置的数,剩下列表中左右元素分别与取出的数比较,如果数比取出的数小,则放到列表的左边;如果数比取出的大,则放在列表右边
- 堆排序
- 归并排序
#么人用的排序
- 基数排序
- 希尔排序
- 桶排序
#冒泡排序
- 列表每相邻的数,如果前边的比后边的大,那么交换这两个数
- 算法复杂度 n^2
import random
def bubble_sort(li):
for i in range(len(li) - 1): # i 趟
for j in range(len(li) - i -1): # j 指针
if li[j] > li[j+1]:
li[j],li[j+1] = li[j+1],li[j]
return li
li = list(range(10))
random.shuffle(li)
obj = bubble_sort(li)
print(obj)
冒泡排序
# 选择排序
- 一趟遍历记录最小的数,放到第一个位置;再一趟遍历记录剩余列表中最小的数,继续放置...
- 时间复杂度 O(n^2)
def select_sort(li):
for i in range(len(li) - 1): #i 趟
min_loc = i
# 找i+1位置到最后面位置内最小的数
for j in range(i+1,len(li)):
if li[j] < li[min_loc]:
min_loc = j
# 和无序区第一个数作交换
li[min_loc],li[i] = li[i],li[min_loc]
return li
obj = select_sort([1,8,6,2,5,3])
print(obj)
选择排序
#插入排序
- 列表被分为有序区和无序区 最初有序区只有一个元素
- 每次从无序区选择一个元素 插入到有序区的位置 直到无序区变空
#方式一:
def insert_sort(li):
for i in range(1,len(li)): # i 代表每次摸到牌的下标
tmp = li[i]
j = i-1 # j代表手里最后一张牌的下标
while True:
if j<0 or tmp>=li[j]:
break
li[j+1] = li[j]
j -= 1
li[j+1] = tmp
return li
obj = insert_sort([1,8,6,2,5,3])
print(obj)
#方式二:
def insert_sort(li):
for i in range(1,len(li)): # i 代表每次摸到牌的下标
tmp = li[i]
j = i-1 # j代表手里最后一张牌的下标
while j>=0 and tmp<li[j]:
li[j+1] = li[j]
j -= 1
li[j+1] = tmp
return li
obj = insert_sort([1,8,6,2,5,3])
print(obj)
此代码无效(....)
def partition(data,left,right):
tmp = data[left]
while left < right:
# right 左移动
while left < right and data[right] >= tmp: #如果low和high没有相遇且后面的数一直大于第一个数 就循环
right -=1
data[left] = data[right]
# left 右移动
while left < right and data[left] <= tmp: #如果low和high没有相遇且后面的数一直大于第一个数 就循环
left +=1
data[right] = data[left]
data[left] = tmp
return left
def quick_sork(data,left,right):
if left <right:
mid = partition(data,left,right)
quick_sork(data,left,mid-1)
quick_sork(data,mid+1,right)
return data
alist = [33,22,11,55,33,666,55,44,33,22,980]
obj = quick_sork(alist,0,len(alist)-1)
print(ob
def _sift(li, low, high):
"""
:param li:
:param low: 堆根节点的位置
:param high: 堆最有一个节点的位置
:return:
"""
i = low # 父亲的位置
j = 2 * i + 1 # 孩子的位置
tmp = li[low] # 原省长
while j <= high:
if j + 1 <= high and li[j + 1] > li[j]: # 如果右孩子存在并且右孩子更大
j += 1
if tmp < li[j]: # 如果原省长比孩子小
li[i] = li[j] # 把孩子向上移动一层
i = j
j = 2 * i + 1
else:
li[i] = tmp # 省长放到对应的位置上(干部)
break
else:
li[i] = tmp # 省长放到对应的位置上(村民/叶子节点)
def sift(li, low, high):
"""
:param li:
:param low: 堆根节点的位置
:param high: 堆最有一个节点的位置
:return:
"""
i = low # 父亲的位置
j = 2 * i + 1 # 孩子的位置
tmp = li[low] # 原省长
while j <= high:
if j + 1 <= high and li[j+1] > li[j]: # 如果右孩子存在并且右孩子更大
j += 1
if tmp < li[j]: # 如果原省长比孩子小
li[i] = li[j] # 把孩子向上移动一层
i = j
j = 2 * i + 1
else:
break
li[i] = t
def partition(data,left,right):
tmp = data[left]
while left < right:
#right 左移动
while left < right and data[right] >= tmp:
right -= 1
data[left] = data[right]
#left 右移动
while left < right and data[left] <= tmp:
left += 1
data[right] = data[left]
data[left] = tmp
return left
def quick_sork(data,left,right):
if left < right:
mid = partition(data,left,right)
quick_sork(data,left,mid-1)
quick_sork(data,mid+1,right)
return data
list = [33,22,11,55,33,666,55,44,33,22,980]
obj = quick_sork(list,0,len(list)-1)
print(obj)
快速排序
#方式一
def insert_sort(li):
for i in range(1,len(li)):
tmp = li[i]
j = i - 1
while True:
if j < 0 or tmp >= li[j]:
break
li[j+1] = li[j]
j -= 1
li[j+1] = tmp
return li
obj = insert_sort([1,8,6,2,5,3])
print(obj)
#方式二
def insert_sort(li):
for i in range(1,len(li)):
tmp = li[i]
j = i - 1
while j >= 0 and tmp < li[j]:
li[j+1] = li[j]
j -= 1
li[j+1] = tmp
return li
obj = insert_sort([1,8,6,2,5,3])
print(obj)
插入排序
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
timewrap.py
from timewrap import *
import random
def _sift(li, low, high):
"""
:param li:
:param low: 堆根节点的位置
:param high: 堆最有一个节点的位置
:return:
"""
i = low # 父亲的位置
j = 2 * i + 1 # 孩子的位置
tmp = li[low] # 原省长
while j <= high:
if j + 1 <= high and li[j + 1] > li[j]: # 如果右孩子存在并且右孩子更大
j += 1
if tmp < li[j]: # 如果原省长比孩子小
li[i] = li[j] # 把孩子向上移动一层
i = j
j = 2 * i + 1
else:
li[i] = tmp # 省长放到对应的位置上(干部)
break
else:
li[i] = tmp # 省长放到对应的位置上(村民/叶子节点)
def sift(li, low, high):
"""
:param li:
:param low: 堆根节点的位置
:param high: 堆最有一个节点的位置
:return:
"""
i = low # 父亲的位置
j = 2 * i + 1 # 孩子的位置
tmp = li[low] # 原省长
while j <= high:
if j + 1 <= high and li[j+1] > li[j]: # 如果右孩子存在并且右孩子更大
j += 1
if tmp < li[j]: # 如果原省长比孩子小
li[i] = li[j] # 把孩子向上移动一层
i = j
j = 2 * i + 1
else:
break
li[i] = tmp
@cal_time
def heap_sort(li):
n = len(li)
# 1. 建堆
for i in range(n//2-1, -1, -1):
sift(li, i, n-1)
# 2. 挨个出数
for j in range(n-1, -1, -1): # j表示堆最后一个元素的位置
li[0], li[j] = li[j], li[0]
# 堆的大小少了一个元素 (j-1)
sift(li, 0, j-1)
li = list(range(10000))
random.shuffle(li)
heap_sort(li)
print(li)
堆排序
import random
from timewrap import *
import copy
import sys
def merge(li, low, mid, high):
i = low
j = mid + 1
ltmp = []
while i <= mid and j <= high:
if li[i] < li[j]:
ltmp.append(li[i])
i += 1
else:
ltmp.append(li[j])
j += 1
while i <= mid:
ltmp.append(li[i])
i += 1
while j <= high:
ltmp.append(li[j])
j += 1
li[low:high+1] = ltmp
def _merge_sort(li, low, high):
if low < high: # 至少两个元素
mid = (low + high) // 2
_merge_sort(li, low, mid)
_merge_sort(li, mid+1, high)
merge(li, low, mid, high)
print(li[low:high+1])
def merge_sort(li):
return _merge_sort(li, 0, len(li)-1)
li = list(range(16))
random.shuffle(li)
print(li)
merge_sort(li)
print(li)
归并排序
NB三人组小结
三种排序算法的时间复杂度都是 O(nlogn)
一般情况下,就运行时间而言:
快速排序 < 归并排序 < 堆排序
三种排序算法的缺点:
快速排序:极端情况下排序效率低
归并排序:需要额外的内存开销
堆排序:在快的排序算法中相对较慢