数据结构与算法
程序 = 数据结构+算法
时间复杂度是用来估计算法运行时间的一个式子(单位)
一般来说(与机器与问题规模有关),时间复杂度高的算法比时间复杂度低的算法慢
常见的时间复杂度排序
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
复杂问题的时间复杂度
O(n!)
O(2n)
O(nn)
判断时间复杂度
确定问题规模
循环减半过程——logn
k层关于n的循环——nk
空间复杂度:用来评估算法内存占用大小的式子
空间复杂度的表示方式与时间复杂度完全一样
算法使用了几个变量:O(1)
算法使用了长度为1的一维列表:O(n)
算法使用了m行n列的二维列表:O(mn)
空间换时间
1.递归
调用自身
结束条件
def fun1(n): if n > 0: print(n) fun1(n-1) def fun2(n): if n > 0: fun2(n-1) print(n) fun1(3) print('-'*30) fun2(3)
递归问题:汉诺塔问题:
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')
汉诺塔移动次数递推式
h(x)=2h(x-1)+1
2.列表查找
输入:列表、待查找元素
输出:元素下标,未查找到时返回(None或者-1)
内置列表查找元素:index( )
(1)顺序查找:也叫线性查找,从列表的第一个元素开始,顺序进行搜索,直到找到元素或者搜索到列表的最后一个元素为止。
时间复杂度: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,4,5,6],5)) # 时间复杂度:O(n)
(2)二分查找:又叫折半查找,从有序列表的初始候选区li[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半
时间复杂度: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: # li[mid] < val: # 待查找的值在mid右侧 left = mid +1 else: return None print(binary_search([1,2,3,4,5,6,7,8,9],3))
3.排序
将一组无序的记录序列调整为有序的记录序列
列表排序:将无序列表转换为有序列表
输入:列表
输出:有序列表
升序与降序
内置排序函数:sort( )
(1)冒泡排序
默认升序排序
列表每相邻的两个数,如果前面的数比后面的大,则交换这两个数
一趟排序完成后,无序区减少一个数,有序区增加一个数
时间复杂度:O(n2)
import random def bubble_sort(li): for i in range(len(li)-1): # 第几趟 exchange = False for j in range(len(li)-i-1): # 每一趟的指针从0开始 if li[j] > li[j+1]: # 箭头指的数大于后面的一个数 li[j+1],li[j] = li[j],li[j+1] # 则交换 exchange = True print(li) if not exchange: return # li = [random.randint(0,10000) for i in range(10)] li = [1,2,3,4,33,5] print(li) bubble_sort(li) print(li)
(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*n) def select_sort(li): for i in range(len(li)-1): # 第几趟,第i趟的无序区的第一个数的下标是i min_loc = i # 假定无序区的第一个数是最小的数 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] # 将找到的最小的数的下标与无序区的第一个数换位置 print(li) li = [22,11,2,5,56,67,88,44,55,6] print(li) select_sort(li) print(li)
(3)插入排序
初始时手里只有一张牌,每次从无序区摸一张牌,插到手里已有牌的正确位置
时间复杂度:O(n2)
插入排序升序程序
def insert_sort(li): for i in range(1,len(li)): # i表示摸到的牌的下标,第一张牌默认为手里的牌(下标为0的位置),所以摸到的牌从1开始 tmp = li[i] #tmp表示摸到的牌,使摸到的牌不能被覆盖 j = i-1 # j指的就是手里的牌 while j >= 0 and li[j] > tmp: #手里的牌为第一张或者之后并且摸到的牌比手里的小 li[j+1] = li[j] # 将牌往右移 j -= 1 # j从左向右遍历 li[j+1] = tmp #摸到的牌比手里的牌大,将摸到的牌插入到比他小的牌的位置 print(li) #关键在于找插入位置 #区分有序区和无序区 li = [1,6,2,8,99,23,22,44,55] print(li) insert_sort(li) print(li)
插入排序降序程序
def insert_sort(li): for i in range(1,len(li)): # i表示摸到的牌的下标,第一张牌默认为手里的牌(下标为0的位置),所以摸到的牌从1开始 tmp = li[i] #tmp表示摸到的牌,使摸到的牌不能被覆盖 j = i-1 # j指的就是手里的牌 while j >= 0 and li[j] < tmp: #手里的牌为第一张或者之后并且摸到的牌比手里的小 li[j+1] = li[j] # 将牌往右移 j -= 1 # j从左向右遍历 li[j+1] = tmp #摸到的牌比手里的牌大,将摸到的牌插入到比他小的牌的位置 print(li) #关键在于找插入位置 #区分有序区和无序区 li = [1,6,2,8,99,23,22,44,55] print(li) insert_sort(li) print(li)