算法图解

chapter1 二分查找

#1.2二分查找
def binary_search(list,item):
    low = 0
    hight = len(list)-1

    while low <= hight:#只要范围没有缩小到只包含一个元素
        mid = (low + hight) // 2 # 向下取整也可以使用int(x)的形式
        guess = list[mid]
        if guess == item:
            return item
        if guess > item:#猜大了
            hight = mid -1
        else:
            low = mid + 1#猜小了
    return None

my_list = [1,3,5,7,9]

print(binary_search(my_list,3))
print(binary_search(my_list,-1))
3
None

chapter2 选择排序

# #选择排序(从小到大)
def selectionSort(arr):
    for i in range(len(arr)):#选择多少次
        min_idx = i#假设最小元素的索引为i
        for j in range(i + 1, len(arr)):
            if arr[min_idx] > arr[j]:
                min_idx = j

        arr[i], arr[min_idx] = arr[min_idx], arr[i]
    return arr

arr = [98, 23, 12, 43, 59]
print("排序后的数组:",selectionSort(arr))
排序后的数组: [12, 23, 43, 59, 98]

chaptet3 递归

##3.1递归
#寻找盒子中的钥匙
"""方法1:使用while循环"""
from numpy import empty


def look_for_key(main_box):
    pile = main_box.make_a_pile_to_look_through()
    while pile is not empty:
        box = pile.grab_a_box()
        for item in box:
            if item.is_a_box():
                pile.append(item)
            elif item.is_a_key():
                print("found the key")
"""方法二:使用递归"""
def look_for_kry(box):
    for item in box:
        if item.is_a_box():
            look_for_key(item)#递归
        elif item.is_a_key():
            print("found the key")

##3.2 基线条件和递归体条件
"""
编写递归函数是,必须设置结束条件
递归函数包含两部分:
    1.基线部分(函数不再调用自己)
    2.递归条件(函数调用自己)
"""

def countdown(i):
    print(i)
    if i <= 0: #基线条件
        return
    else:
        countdown(i-1)  #递归条件
        
print(countdown(-1))
print(countdown(1))
-1
None
1
0
None
#3.3 栈
"""
栈的两种操作:压入和弹出
所有函数调用都进入调用栈
调用栈可能很长,这将占用大量的内存
"""
#3.3.1 调用栈
def greet(name):
    print("hello, "+name+" !")
    greet2(name)
    print("getting ready to say bye...")
    bye()

def greet2(name):
    print("how are you, "+name+" ?")

def bye():
        print("ok bye!")

greet("maggie") #调用函数变量“maggie”存储到内存中,打印    hello, maggie !
                    #调用函数greet2,打印 how are you, maggie ?greet函数暂停并处于未完成状态
                     # 执行玩greet2后返回greet 打印 getting ready to say bye...
                      #在调用函数bye 打印ok bye!
hello, maggie !
how are you, maggie ?
getting ready to say bye...
ok bye!

#3.3.2 递归调用栈
def fact(x): #x!
    if x == 1:
        return 1
    else:
        return x * fact(x-1)    #递归调用

6

chapter 4快速排序

##4.1分而治之(divide and conquer,D&C)
"""
分而治之是一种通用的算法
分而治之解决问题的过程包括两个步骤(工作原理):
    step1.找出简单的基线条件
    step2.确定如何缩小规模,使其符合基线条件
"""
"""
#Q:将一块地均匀分成方块且分出的方块要尽可能大
#A: step1.找出这块地可容纳的最大方块
    step2.在剩余的地中找出可容纳的最大方块
    step3.一直递归,直至将地分完
"""

#使用循环将一个数字数组相加
def  sum(arr):
    total = 0
    for x in arr:
        total += x
    return total

print(sum([2,4,6]))

#使用递归函数完成数组相加
"""
step1:找出简单的基线条件(数组不包含任何元素或只包含一个元素)
step2:缩小规模(计算数组列表中除第一个数字外的其他数字的总和,将其与第一个数字相加)
"""
def sum_rexursion(arr):
    if arr == []:
        return 0
    return arr[0]+sum(arr[1:])

print(sum([2,4,6]))

#%%
def sum(arr):
    if arr == []:
        return 0
    a = arr[0]
    del arr[0] #del删除的是变量,而不是数据
    return sum(arr)+a
print(sum([2,4,6]))
12
12
12
##练习
#4.2 编写一个递归函数来计算列表包含的元素数
def lenList(list):
    if list == []: #基线条件:列表中的元素是否为空
        return 0
    else:
        return 1+lenList(list[1:])

print("列表包含的元素个数为:",lenList([2,4,6]))
列表包含的元素个数为: 3

#4.3 找出列表中的最大数字
def find_max(list):
    tmp = list.pop(0)
    if  list == []:
        return  tmp
    max = find_max(list)
    if max > tmp:
        return max
    else:
        return tmp

print("找出列表中最大的元素:",find_max([2,4,6,7,9]))
找出列表中最大的元素: 9
#4.4递归实现二分查找

def HalfSearch(OrdderedList, key, left, right):
    if left > right:#二分查找算法要求数组按顺序排列
        return None
    mid = (left + right)//2
    if key == OrdderedList[mid]:
        return mid
    elif key > OrdderedList[mid]: #如果关键字大于猜测的数值
        return HalfSearch(OrdderedList, key, mid+1, right)
    else: #如果关键字小于猜测的数值
        return HalfSearch(OrdderedList, key, left, mid-1)

a = [1,2,3,4,5,6,7,8,9]
#print(len(a))
user_input = int(input("请输入你要查询的值:"))


left = 0
right = len(a)-1

index = HalfSearch(a, user_input, left, right)#调用递归二分查找算法
print(index)
请输入你要查询的值:3
2
#4.2 快速排序
"""
快速排序算法也运用了分而治之的思想
快速排序算法的步骤:
    step1.选择基准值
    step2.将数组分成两个子数组:小于基准值的元素和大于基准值的元素
    step3.对这两个子数组进行快速排序

"""
def quickSort(arr):
    if len(arr)<2:
        return arr #基线条件:空的或只包含一个元素的数组是“有序”的
    else:
        pivot = arr[0] #递归条件(pivot)为基准值
        less = [i for i in arr[1:] if i<= pivot] #由所有小于基准值的元素组成的子数组
        greater = [i for i in arr[1:] if i> pivot] #由所有大于基准值的元素组成的子数组
        return quickSort(less) + [pivot] + quickSort(greater)

print("快速排序算法:",quickSort([10,5,2,3]))
快速排序算法: [2, 3, 5, 10]
#4.3 大O
"""
c*n:c是算法所需的固定时间量
快速查找的常量比合并查找小,虽然运行时间都为O(nlogn),快速查找的速度将更快

"""

chapter 5 散列表

#5.1 散列函数
"""
散列函数:将输入映射到数字

eg:商店售货员如何快速的知道商品的价格
1.创建一个空数组
2.将商品名字(苹果)加入到数组中,散列函数输出为3,故商品(苹果)的价格存储到数组的索引3处
...

散列函数:将同样的输入映射到相同的索引,将不同的输入映射到不同的索引,知道数组的大小,只返回有效的索引

"""

"""
散列表:散列函数+数组
散列表使用散列函数来确定元素的存储位置
散列表也被称作散列映射、映射、字典和关联数组(python提供的散列表表现为字典)
散列表由键和值组成
"""

#5.2 应用案例
"""
5.2.1 将散列表用于查找
"""
#text01
book = dict()
book['apple'] = 0.67
book['milk'] = 1.49

print(book['apple'])
0.67

#text02
phone_book = {} #创建散列表的快捷方式
phone_book['jenny'] = 864321
phone_book['emergency'] = 911
print(phone_book['jenny'])

864321
#5.2.2 防止重复
voted = {} #创建一个空的散列表

def check_voter(name):
    if voted.get(name): #get函数返回指定键的值
        print("kick them out!")
    else:
        voted[name] = True
        print("let them vote!")

check_voter("tom")
check_voter("mike")
check_voter("mike")

let them vote!
let them vote!
kick them out!
#5.2.3 散列表作为缓存
cache = {}


def get_data_from_server(ur1):
    pass


def get_page(ur1):
    if cache.get(ur1):
        return cache[ur1]
    else:
        data = get_data_from_server(ur1)
        cache[ur1] = data

在这里插入代码片

#5.2.4 小结
"""
散列表适用于:模拟映射关系;防止重复;缓存数据
"""

##5.3 冲突 & 5.4性能
"""
冲突:给两个分配的位置相同
处理冲突最简单的方法:如果两个键映射到同一个位置,就在这个位置存储一个链表
避免冲突:较低的填装银子,良好的散列函数
一旦填装银子超过0.7,就调整散列表的长度
"""
#练习5.5
tel = {}

tel['Esther'] = 123
tel['Ben'] = 234
tel['Bob'] = 456
tel['Dan'] = 678

print(tel['Bob'] )

456
#练习5.6

power = dict()

power['A'] = 10
power['AA'] = 100
power['AAA'] = 500
power['AAAA'] = 1000


print(power['AA'])

100
#练习5.7
book = dict()

book['Apple'] = 'Maus'
book['Bob'] = 'Fun Home'

print(book['Bob'])
Fun Home

chapter6.广度优先搜索

#6.1 图简介
""""
解决最小路径问题:(广度优先搜素算法)
    1.使用图连建立问题模型
    2.使用广度有限搜索解决问题
"""
#6.2 图是什么
"""
图模拟一组连接,图由节点和边组成,一个节点可能与众多节点直接相连,这些节点被称作邻居
"""
#6.3 广度优先搜索
"""
广度优先搜索可以解决两类问题:
    1.从节点A出发,有前往节点B的路劲吗?
    2.从节点A出发,前往节点B的哪条路径最短?
广度优先搜索需要按添加顺序进行检查,用队列来实现
队列只支持两种操作:入队和出队
队列(FIFO)是先进先出,栈(LIFO)是后近先出
广度有限搜索的运行时间为O(V+E),其中V为顶点数,E为边数
使用拓扑排序创建一个有序的列表
树是一种特殊的图,没有往后指的边
"""

"""------从人际关系网中找到芒果销售商------"""
#6.4 实现图
"""
有向图的边为箭头,箭头的方向指定了关系的方向
无向图的边不带箭头,其中的关系是双向的
"""
# graph ={} #散列表是无序的
# graph["you"] = ["alice","bob","claire"]
# graph["bob"] = ["anuj",'peggy']
# graph["claire"] = ["thom","jonny"]
# graph["anuj"] = []
# graph["peggy"] = []
# graph["thom"] = []
# graph["jonny"] = []
"""------从人际关系网中找到芒果销售商------"""
#6.5 实现算法
from collections import deque #使用函数deque创建一个双列表

graph ={} #散列表是无序的
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []


def person_is_seller(name):  # 判断一个人是否是芒果销售商
    return name[-1] == 'm'

def search(name):
    search_queue = deque() #创建一个队列
    search_queue += graph[name] #将name的邻居都加入到这个搜索队列
    print(search_queue)

    searched = [] #记录检查过的人
    while search_queue: #如果队列不为空
        person = search_queue.popleft() #就取出其中一个人 popleft是用于collections中
        print(person)
        # print(search_queue)

        if not person in searched: #如果不是已经检查过的人
            if person_is_seller(person): #检查这个人是否是芒果销售商
                print(person + " is a mago seller!")
                return True
            else:
                search_queue += graph[person] #不是芒果销售商,将这个人的朋友都加入到搜索列表
                print(search_queue)
                searched.append(person) #将这个人记录到已经检查过的人中
                print(searched)
    return False

search("you")
deque(['anuj', 'peggy', 'thom', 'jonny'])
['alice', 'bob', 'claire', 'peggy']
anuj
deque(['peggy', 'thom', 'jonny'])
['alice', 'bob', 'claire', 'peggy', 'anuj']
peggy
thom
thom is a mago seller!

chapter7.狄克斯特拉算法

"""
广度优先搜索:在非加权图中查找最短路径
狄克斯特拉算法: 在加权图中查找最短路径,即总权重最小的路径
                (但权值必须为正值,权值为负,使用贝尔曼—福德算法)
                避免环,只适用于有向无环图

"""


"""
狄克斯特拉算法的4个步骤:
        1.找出”最便宜"的节点
        2.更新该节点的邻居的开销
        3.重复这个过程
        4.计算最短路径
"""

"""------代码实现------"""
"""---1.将节点的所有邻居都存储在散列表中---"""

graph =  {}#构造全图字典
graph["start"] = {} #使用字典中的字典
graph["start"]["a"] = 6
graph["start"]["b"] = 2
print(graph["start"].keys()) #获取起点的所有邻居
print(graph["start"]["a"]) #查看节点的值
print(graph["start"]["b"])

graph["a"] = {} #添加其他节点及邻居
graph["a"]["fin"] = 1

graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["fin"] = 5

graph["fin"] = {} #终点没有任何邻居

"""---2.用一个散列表来存储每个节点的开销---"""
#节点的开销是指从起点出发前往该节点需要多长时间

infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity

"""---3.用一个散列表来存储父亲节点---"""
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None

"""---4.用一个数组记录处理过的节点---"""
processed = []


"""---5.找出开销最低的节点---"""
def find_lowest_cost_node(costs):
    lowest_cost = float("inf") #初始化最低花销
    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

"""---6.算法---"""
"""
1.只要还有需要处理的节点,
2.获取离起点最近的节点,
3.更新其邻居的开销
4.如果有邻居的开销被更新,同时更新其父节点
5.该节点标记为处理过

"""
def main(graph,costs,parents,processed,infinity):
    node = find_lowest_cost_node(costs) #在未处理过的节点中找出开销最小的节点
    while node is not None: #while循环在所有节点都被处理过后结束
        cost = costs[node]
        neighbors = graph[node]
        for n in neighbors.keys(): #遍历当前节点的所有邻居
            new_cost = cost + neighbors[n]
            if costs[n] > new_cost: #如果经当前节点前往该邻居更近
                costs[n] = new_cost #就更行该邻居的开销
                parents[n] = node  #同时将该邻居的父节设置为当前节点
        processed.append(node)  #将当前节点标记为处理过
        node = find_lowest_cost_node(costs) #找出接下来要处理的节点并循环

main(graph,costs,parents,processed,infinity)
print(costs)
print(parents)



dict_keys(['a', 'b'])
6
2
{'a': 5, 'b': 2, 'fin': 6}
{'a': 'b', 'b': 'start', 'fin': 'a'}

chapter8 贪婪算法

""""贪婪算法"""
""""
Q1.如何选出尽可能多且时间不冲突的课程
1.选出结束最早的课
2.接下来选择第一堂课结束后才开始的课
3.重复这样做

贪婪算法的优点:简单易行(每步都选择局部最优解,最终得到的就是全局最优解)
但在有些情况下,贪婪算法只能得到近似解,不能获得最优解

"""
"""
集合覆盖问题
Q:覆盖全美50个州的最小广播台集合
1.列出每个可能广播台的集合,这被称为幂集
2.在这些集合中,选出覆盖全美50个州的最小集合

近似算法——利用贪婪算法得到非常接近的解
1.选出覆盖最多州的广播电视台
2.重复第一步,直到覆盖了所有州

判断近似算法的优劣标准:
1.速度有多快
2.得到的近似解与最优解的接近程度

"""

#1.准备工作
states_needed = set(["mt","wa","or","id","nv","ut"]) #创建一个列表(转换为集合),包含要覆盖的州

stations ={} #散列表创建广播台清单

stations["kone"] = {"id","nv","ut"}
stations["ktwo"] = {"wa","id","mt"}
stations["kthree"] = {"or","nv","ca"}
stations["kfour"] = {"nv","ut"}


final_stations = set() #使用集合来存储最终选择的广播台

#2.计算答案
while states_needed:
    best_station = None #覆盖最多州的广播电视台初始化
    states_covered = set() #该广播太覆盖的搜友未覆盖的州

    for station, states in stations.items(): #items()函数以列表返回可遍历的(键, 值) 元组数组
        covered = states_needed & states #交集 同时出现在states_needded和state中的州
        if len(covered) > len(states_covered): #检查该台覆盖的州是否比best_station多
            best_station = station
            states_covered = covered
            states_needed -= states_covered
            final_stations.add(best_station)

print(final_stations)
{'ktwo', 'kone', 'kthree'}
"""NP 完全问题"""
"""
求解NP完全问题:近似解算法
判断问题是否为NP完全问题:
1.元素较少时算法的运行速度非常快,但随着元素数量的增加,速度会变得非常慢
2.涉及"所有组合"的问题
3.不能将问题分成小问题,必须考虑其他情况
4.如果问题涉及序列,且难以解决(旅行商问题中的城市序列)
5.如果问题设计集合且难以解决
6.如果问题课转换为集合全覆盖或旅行商问题


"""

chapter10 动态规划

"""ch09 动态规划"""

"""
动态规划先解决子问题,再逐步解决大问题
在动态规划时,要么考虑拿走整件商品,要么考虑不拿,而没法判断该不该拿走商品的一部分,这种情况应使用贪婪算法
仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用
动态规划可帮助在给定约束条件下找到最优解
每种动态规划方案都涉及网络
单元格中的值通常就是需要优化的值
每个单元格都市一个子问题
没有放之四海皆准的计算动态规划解决方案的公式
"""
"""
费曼算法;
1.将问题写下来
2.好好思考
3.将答案写下来


"""
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w要变强

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值