Reference: 算法图解
git 代码
大O表示法
查找算法-二分查找
使用条件: 对于有序的数组
算法的时间:
O
(
l
o
g
n
)
O(log n)
O(logn) 或者
l
o
g
N
+
1
logN + 1
logN+1次 [ 大O表示法说明了算法的增速 ]
import numpy as np
def binary_search( memory,desired_number):
index_lower = 0
index_upper = len(memory)-1
index_chosen = int ( (index_lower+index_upper)/2 )
# python 3.6 中 不论类型 / 除法的结果都是 float型,但是List的index只能是整数,因此需要使用强制类型转换
# index_chosen = (index_lower+index_upper)//2 // 整数除法会考虑输入的类型,两个int类型结果会自动取整
while index_lower<=index_upper:
if memory[index_chosen] == desired_number:
return index_chosen
elif memory[index_chosen] < desired_number:
index_lower = index_chosen+1
else:
index_upper = index_chosen-1
index_chosen = int ( (index_upper+index_lower)/2 )
# index_chosen = (index_upper+index_lower)//2
return None
""" test """
memory = list( np.random.randint(100,size=30) )
memory.sort()
desired_number = np.random.randint(100,size=1)
print('memory: ',memory);
print('desired_number: ',desired_number)
print('index: ', binary_search(memory,desired_number) )
选择排序
数组是提前预留了固定数目的空间。如果我们对要存储的数据数量难以提前知道,那么就会造成 内存浪费(数组提前定义过大)或者在中途定义更大的数组,然后对数组数据全部进行转移的情况,浪费时间。因此对于数目未知的数据进行存储时,还是尽量选择可以柔性的链表。另外对于数据插入来说,链表可以通过修改地址的内容,实现链接的顺序改变。而对于数组,我们必须将数组的内容进行后移,才能实现数据的插入。
数组 | 链表 | |
读取 | O(1) | O(n) |
插入 | O(n) | O(1) |
删除 | O(n) | O(1) |
因此,对于随机读取次数较多的数据,我们还是最好使用数组;对于插入\删除比较多的情况,我们最好还是用链表。
选择排序通过每次选出数组中的最大元素,然后进行剔除;再选出剩余元素的最大元素,再进行剔除的方式进行 进行排序 O ( n 2 ) O(n^2) O(n2)。
# 列表的删除命令 a.pop(index) 默认 a.pop()实现末尾元素的删除
def search_small(array): # 查找列表中最小元素以及对应的index
array_small = array[0]
array_small_index = 0
for i in range(len(array)):
if array[i] < array_small:
array_small_index = i
array_small = array[i]
return [array_small,array_small_index]
def sort_ascend(array):
array_sorted = []
for i in range(len(array)):
component,index = search_small(array)
array_sorted.append(component)
array.pop(index)# 通过index剔除数组中的最小元素
return array_sorted
print( sort_ascend([2,1,5,7,9,3,3]) )
递归函数
递归包含基线条件和递归条件。 递归条件是函数调用自己的条件; 基线条件是函数不在调用自己的条件,避免形成无限循环。
调用栈
栈: 后进先出。
我们可以将函数看作一个物体,调用函数的时候我们就会创建一个内存空间,用于存储函数中的的变量,通过内存空间中的变量去实现其他的语句。这个物体(内存空间)就压入栈中,在栈顶(表示正在执行的内容)。
那么当函数包含有其他函数的时候,就创建其他的内存空间生成该函数物体,然后将该物体压入栈中,此时该函数成为了栈顶。我们只有该函数内容都执行完,才能将该函数从栈中弹出(将该内存空间释放),然后执行之前的函数。
对于递归调用栈:
因为函数满足递归条件时,函数在一直递归,一直产生新的函数内存出现在栈顶,只有出现满足基线条件才会停止产生新的函数内存,然后才会将内存弹出,从而慢慢递归回去父函数。
这样的方式会占用很大的内存,因此不是所有的递归函数都可以实现。
分而治之
- 找到简单的基线条件;
- 将问题缩小规模(相同的问题,更小的规模),使其符合基线条件
快速排序
将N个数字的排序转化为1个数字在有序数组中的排序,同时采用递归思想。
def quick_sort(array):
if len(array)>=2:
last = array.pop()
lower = []
greater = []
for component in array:
if last<component:
greater.append(component)
else:
lower.append(component)
return quick_sort(lower) + [last] + quick_sort(greater)
else:
return array
print( quick_sort([2,4,3,7,1,0,-1,-5,-8,-11,-18,5]) )
合并排序
def MergeSort(array):
if len(array)>=2:
array_front = MergeSort(array[:len(array)//2])
array_back = MergeSort(array[len(array)//2:])
array_new = []
for i in range(len(array_front)):
for j in range(len(array_back)):
if array_front[0]<=array_back[0]:
array_new.append(array_front[0])
array_front.pop(0)
break
else:
array_new.append(array_back[0])
array_back.pop(0)
if len(array_front)==0:
array_new = array_new + array_back
break
elif len(array_back) ==0:
array_new = array_new + array_front
break
return array_new
else:
return array
print(MergeSort([2,4,3,7,1,0,-1,-5,-8,-11,-18,5]))
####################################################
###更好的写法
####################################################
def MergeSort(array):
if len(array)>=2:
array_front = MergeSort(array[:len(array)//2])
array_back = MergeSort(array[len(array)//2:])
array_new = []
while len(array_front)>0 and len(array_back)>0:
if array_front[0]<=array_back[0]:
array_new.append(array_front[0])
array_front.pop(0)
else:
array_new.append(array_back[0])
array_back.pop(0)
array_new += array_front
array_new += array_back
return array_new
else:
return array
print(MergeSort([2,4,3,7,1,0,-1,-5,-8,-11,-18,5]))
冒泡排序
插入排序
散列函数
散列函数的特点:
- 散列函数将一个字符串(名称,key)对应到一个特定的内存地址,避免了数组中的查找等操作,一步到位。
- 散列函数实现了 固定字符串 会生成固定的内存地址;不同的字符串会生成不同的内存地址
- 散列函数根据字符串生成的内存地址是在可用范围以内的地址,不会超出范围。
Python 中这样的散列函数 就是字典 dict。
voted = {}
value = voted.get("tom") # 如果"tom"在字典中,那就返回dict值; 如果"tom"不在字典中,那就返回None