数据结构与算法基础(基于python)

用大O法表示运行时间,log都表示log2(以2为底的对数)
所有的算法都是基于python写的

一、二分查找法:

1、输入:一个有序的元素列表
2、输出:如果要查找的元素包含在列表中,二分查找返回其位置,否则返回NULL.
3、使用二分查找,每次排除一半的数字
4、算法过程:检查中间元素,若小了,就改变low,如果大了就修改high
5、完整代码如下:

def binary_search(list,item):
    low=0
    high=len(list)-1

    while low<=high:      #只要范围没有缩小到只剩一个元素
        mid=(low+high)/2
        guess=list[mid]
        if guess==item:
            return mid
        if guess > item:
            high=mid-1
        if guess < item:
            low=mid+1
    return None

//测试一下
mylist=[1,3,5,7,9]
print binary_search(mylist,3)
print binary_search(mylist,-1)

6、运行时间:大O表示法,指出了最糟情况下的运行时间
①简单查找:O(n)
②二分查找:O(log n)
③快速排序:O(n*log n)
④选择排序:O(n^2)
⑤旅行商问题:一种非常慢的算法O(n!)
旅行商问题:找最短路径

二、选择排序

1、涉及到数据结构——数组和链表

①数组:内存单位相连,固定好了一定的内存空间,如果不够不可以临时加,只能重新划定一个连续内存空间
②链表:元素可存储在内存的任何地方,每一个元素都存储了下一个元素的地址,优势在插入元素方面。但是当你要访问某一个元素时,你就得从链表头开始一个个访问过去,对于跳跃真的很不方便,因为你不知道后面的元素的地址,只能一个个往后推着走。

运行时间 数组 链表
读取 O(1) O(n)
插入 O(n) O(1)
删除 O(n) O(1)

2、插入(删除)

①使用链表插入,只需要改变前面那个元素指向的地址,通常都记录了链表的第一个元素和最后一个元素
②使用数组,则必须将后面的元素都向后移,这样感觉会很麻烦,有时候要是数目大于数组的已划分的内存的长度,那就得复制到一个新的数组中了
③链表只能顺序访问,数组支持随机访问
所以,插入用链表,删除同理

3、选择排序思想:

每次从剩下的元素中查找到最大(或最小的元素),之后将元素挑出来放在新列表中接下去的位置中。

4、代码如下:

def findSmallest(arr):
    smallest = arr[0]  #用于存储最小的值
    smallest_index=0   #用于存储最小的索引
    for i in range(1,len(arr)):
        if arr[i]<smallest:
            smallest=arr[i]
            smallest_index=i
        return smallest_index

def selectionSort(arr):
    newArr=[]
    for i in range(len(arr)):
        smallest=fomdSmallest(arr)
        newArr.append(arr.pop(smallest))#找出数组中最小的元素,并将其加入到新数组中,从原来的数组中pop出来
    return newArr

print selectionSort([5,3,6,2,10,85])

以上python中数组的操作比如pop可以参考:https://blog.csdn.net/anneqiqi/article/details/71057069

三、快速排序

1、分而治之D&C

2、递归——让解决方案更清晰

递归指的是函数调用自己,相关数据结构—栈(压栈和出栈),每次调用函数时,计算机都将函数调用所涉及到的所有变量的值存储在内存中,如果陷入没完没了的递归会导致栈溢出。

3、快速排序思想:

①找一个元素作为基准值
②找出比基准值小的元素还有比基准值大的元素,得到——一个由所有小于基准值的数字组成的子数组 + 基准值 + 一个由所有大于基准值的数字组成的子数组
③对子数组继续进行排序(递归调用)
归纳证明:归纳条件和基线条件

4、代码如下:

#O(n log n)
def quicksort(array):
    if len(array) <2:
        return array   #基线条件:为空或者只包含一个元素的数组(有序)
    else:
        plvot=array[0]
        less=[i for i in array[1:] if i<=plvot]
        greater=[i for i in array[1:] if i > plvot]
        return quicksort(less)+[plvot]+quicksort(greater)

print quicksort([10,5,2,6,8,9])

5、关于大O时间

①最佳/平均:O(log n)
②最差:O(n^2)

四、散列表—dict(),由键和值组成

1、散列函数

①散列函数总是将同样的输入映射到相同的索引,每次取相同已知数的值的函数值时都能得到一样的函数值结果
②散列函数将不同的输入映射到不同的索引
③散列函数知道数组多大,只返回有效的索引

2、操作散列表

①创建 number=dict() 或者 number={} ( 一对大括号 )
②添加元素number[“Marry”]=8945968
域名 → IP 就是用散列表
③散列表可以防止重复(键)
④将散列表用作缓存:网站将数据记住,不再需要重新计算获取。
⑤当访问一个网站页面时,它会先检查散列表中是否存储了该页面。

cache={}  #创建散列表

def get_page(url):
    if cache.get(url):    #散列表的get方法
        return cache[url]
    else:
        data=get_data_from_server(url)
        cache[url]=data
        return data

⑥散列表用于模拟映射关系
⑦冲突:如果两个键映射到了同一个位置,就在这个位置存储一个链表
⑧好的散列函数应该做到将键均匀地映射到散列表的不同位置
⑨性能:在平均情况下,散列表执行各种操作的时间都是O(1),是常量时间,并不意味着马上,而是说不管散列表多大,所需的时间都一样

散列表 平均情况 最糟情况 数组 链表
查找 O(1) O(n) O(1) O(n)
插入 O(1) O(n) O(n) O(1)
删除 O(1) O(n) O(n) O(1)

⑩填装因子=散列表包含的元素数 / 位置总数,用于度量散列表中有多少位置是空的,填装因子大于1意味着元素数量超过了数组的位置数。填装因子越低,散列表性能越高。
均匀分布:即尽量让不同的键值拥有不同的散列值

五、广度优先遍历—图算法,

1、作用:非加权图的最短路径

①从节点A出发,有前往节点B的路径吗?
②从节点A出发,前往节点B的哪条路径最短?

2、广度优先遍历思想:

首先在一度关系中搜索,确定一度关系中没有结论后才在二度关系中进行搜索,搜索范围从起点开始逐渐向外延伸

3、涉及到的数据结构—队列:入队、出队 + 散列表(用于映射)

队列的操作
queue() 定义一个空队列,无参数,返回值是空队列。
enqueue(item) 在队列尾部加入一个数据项,参数是数据项,无返回值。
dequeue() 删除队列头部的数据项,不需要参数,返回值是被删除的数据,队列本身有变化。
isEmpty() 检测队列是否为空。无参数,返回布尔值。
size() 返回队列数据项的数量。无参数,返回一个整数。
deque() 双向队列https://www.cnblogs.com/zhenwei66/p/6598996.html

4、代码如下:

from collections import deque

def search(name):
    search_queue = deque()
    search_queue += graph[name]
    searched=[]   #用于记录检查过的人的数组
    while search_queue:
        person=search_queue.popleft()  #获取最左边一个元素,并在队列中删除
        if not person in searched:
            if person_is_seller(person):
                print person + " is a mango seller!"
                return true
            else:
                search_queue += graph[person]  #可能是个数组
                searched.append(person)
    return False

graph={}  #创建一个散列表
graph["you"]=["alice","bob","cici"]
graph["alice"]=["peggy"]
graph["bob"]=["ann"]
........   #表示图的邻居关系,一度关系
search["you"]

5、运行时间:O(V + E)

V:表示顶点数,E表示边数

六、狄克斯特拉算法 Dijkstra

1、作用:

找出权重最小的路径

2、过程:

①找出最便宜的节点,即在最短时间内可以前往的节点
②对于该节点的邻居,检查是否有前往它们的更短路径,如果有,更新开销
③重复这个过程,直到对图中的每个节点都这样做了
④计算最终路径
负权边不适合用狄克斯特拉算法,包含负权边应该使用贝尔曼-福德算法

3、涉及数据结构:散列表

4、代码如下:

def find_lowest_cost_node(costs):
    lowest_cost=float("lnf")
    lowest_cost_node=None
    for node in costs:
        cost=costs[node]
        if cost < lowest_cost and node not in processed:  #如果当前节点的开销更低且未经过处理
            lowest_cost=cost  #将其视为开销最低的节点
            lowest_cost_node=node
    return lowest_cost_node


node = find_lowest_cost_node(costs)#在未处理的节点中找出开销最小的节点
while node is not None:
    cost =costs[node]
    neighbors=graph[node]
    for n in neighbors.keys():       #遍历所有的邻居节点
        new_cost=cost + neighbors[n]
        if costs[n] >new_costs:   #如果经当前节点前往该邻居更近
            costs[n]=new_cost     #更新该邻居的开销
            parents[n]=node      #同时将该邻居的父节点设置为当前节点
    processed.append(node)         #将当前节点加入到处理队列
    node=find_lowest_cost_node(costs)         #找出接下来要处理的节点

七、贪婪算法

八、动态规划

1、将问题分成小问题,并先着手解决小问题,每个动态规划解决方案都设计网格。

九、K最近邻算法—KNN

1、推荐电影?根据喜好?
2、计算两点的距离,可以使用毕达哥斯拉公式
3、涉及机器学习:
① OCR:光学字符识别 ——人脸识别、语音识别
② 垃圾邮箱过滤器—朴素贝叶斯分类器
4、KNN用于分类和回归,需要考虑最近的邻居。分类就是编组,回归就是预测结果

十、其他

1、数:二叉树、B树(平衡树)
B树、平衡树是什么?性能比较好的树
2、反向索引:搜索引擎、搜索
3、傅里叶变换:分析成分,处理信号、压缩音乐;
4、并行算法
5、归并函数
6、布隆过滤器
7、SHA算法
8、Diffie-Hellman算法:公开密钥算法,非对称
9、线性规划:Simplex算法

《算法图解》笔记
https://blog.csdn.net/dongrixinyu/article/details/78775057

猜你喜欢

转载自blog.csdn.net/laon_chan/article/details/79717740
今日推荐