算法学习

2019年春,首先感谢袁国忠先生的《算法图解》,使我踏入算法的殿堂。以下内容都是基于书中的内容的理解和摘抄。

算法是一组完成任务的指令。以下是算法概念

简单查找:从头到尾遍历列表元素。

线性时间:如果列表包含10亿个数字,最多猜10亿次,最多需要猜测的次数与列表长度相同。

大O表示法:指算法速度有多快。表示运行时间为O(_{n})

常见的大O运行时间:

O(logn),也叫对数时间,这样的算法包括二分查找。

O(n),也叫线性时间,这样的算法包括简单查找。

O(n *logn) ,快速排序算法。

O(n^{2}),选择排序,较慢的算法。

O(n!),介绍旅行商问题的解决方案,较慢的算法。 旅行商问题的解决方案,较慢的算法。

算法的速度指的并非时间,而是操作数的增速。
算法的运行时间并不以秒为单位,是从其增速的角度度量的,用大O表示法表示。

1、二分查找

假如你登入知乎知乎必须核实你是否有其网站的账户,因此必须在其数据库中查找你的用户名,更合乎逻辑的做法是——

从中间查找

这就是二分查找,前提是元素列表必须有序,比如从1到100,从小到大,二分查找利用了列表的有序性,减少查找的次数。对于包含n个元素的列表,用二分查找最多需要log_{2}n (对数运算)步。下面是python代码实现:

def binary_search(list, item):
    low = 0               
    high = len(list)      #list中元素的数目            

    while low <= high:
        mid = int((low + high) / 2)  #取中间元素的
        guess = list[mid]
        if guess == item:      #如果列表中间的元素是要找的数字,返回的mid为查找的次数      
            return mid
        elif guess > item:     
            high = mid - 1     #如果中间元素比目标数字大,则以此时的中间元素的前一位为high
        else:
            low = mid + 1      #如果中间元素比目标数字小,则以此时的中间元素的后一位为low
    return None

my_list = [1, 3, 5, 7, 9]
print(binary_search(my_list, 4))
print(binary_search(my_list, 3))

2、选择排序

两种基本的数据结构——数组和链表

需要同时所有元素时,链表的效率很高:你读取第一个元素,根据其中的地址再读取第二元素,以此类推。但如果你需要跳跃,链表的效率真的很低。数组的读取速度很快,链表的插入和删除速度很快。

假如你的计算机存储了很多乐曲。对于每个乐队,你都记录了其作品被播放的次数。

排序算法很有用,因为需要检查的元素数越来越少

选择排序的做法很简单:

  • 选最大(或最小)的一个元素
  • 放在序列的起始位置
  • 再从剩余未排序的元素继续找最大(或最小)元素,放在已排序序列的末尾

大O表示法:O(n^{2})

3、递归

一般递归能解决的问题,while都能解决,如果使用循环,程序的性能可能更高。如果使用的递归,程序可能更容易理解。如何选择要看什么对你来说更重要。

编写递归函数时,必须告诉它何时停止递归,正因为如此,每个递归函数都有两种分:基线条件和递归条件。

递归条件是函数调用自己,基线条件是函数不再调用自己,避免无限循环。

栈:一种数据结构,可以压入和弹出。后进先出概念。所有函数调用都进入调用栈

每个函数调用都要占用一定的内存,如果栈很高,意味着计算机存储了大量函数调用的信息。在这种情况下,你有两种选择。

  • 用循环
  • 用尾递归。

递归用于Haskell等函数式编程(输入输出调用)。

4、快速排序

   快速排序使用分而治之(D&C算法)的策略。C语言中的标准库的qsort用的就是快速排序。

  使用D&C算法两个步骤:

  •    找出基线条件,这种条件必须尽可能简单。
  •    不断将问题分解,直到符合基线条件。

   快速排序的步骤:

  • 选择基准值
  • 将数组分成两个子数组:小于基准值的元素和大于基准值的元素。
  • 对这两个子数组进行快速排序

  归纳证明:基线条件和归纳条件。如何证明我能爬到梯子的最上面?基线条件:我已经站在第一横档上,因为,通过每一次爬一个横档,我就能爬到梯子顶端。归纳条件:如果站在一个横档上,就能将脚放到上面一个横档上。

D&C将问题逐步分解,使用D&C处理列表时,基线条件很可能是空数组或只包含一个元素的数组。

快速排序在最糟情况下,其运行时间为O(n^{2}),平均情况下,运行时间为O(n *logn)

合并排序步骤:

  • 把数组分成两部分,分别进行排序
  • 再把排好序的两部分合并成排序数组

合并排序为O(n *logn),那它一定比快速排序运行快吗?不,实际上还有c*n,c是算法所需的固定时间量,被称为常数。而合并排序的c大于快速排序的。所以快速排序往往查找速度比合并排序更快。

比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(logn)O(n)快得多。

5、散列表

    假如你在杂货店上班,你需要查找苹果价格,怎么快速查找出来呢?你需要一名记住所有商品价格的雇员。而散列表就充当记住所有苹果价格的雇员的角色。

散列函数:无论你给它什么数据,它都还你一个数字。满足以下要求:

  • 必须是一致的。假如你输入apple得到4,每次输入时候,必须是4。
  • 应将不同的输入映射到不同数字。 

使用散列函数和数组创建了一种被称为散列表的数据结构。在面向对象中,散列表为字典,一种映射关系的数据结构。

访问网站时,网址必须转换为IP地址。散列表时提供DNS解析的方式之一。

散列表也可以用于缓存。缓存的工作原理:网站将热点数据存储,不再重新计算和查询。

很多大型网站不仅缓存主页,还缓存about页,Contact页面、Terms and Conditions页面。因此,它需要将页面URL映射到页面数据。

散列表适合用于:

  • 模拟映射关系
  • 防止重复
  • 缓存/记住数据,以免服务器再通过处理来生成它们。

避免冲突:

  • 较低的填装因子
  • 良好的散列函数

6、广度优先搜索

广度优先搜索让你能够找出两样东西之间的最短距离。

  • 编写国际跳棋AI,计算最少走多少步就可获胜。
  • 编写拼写检查器,计算最少编辑多少个地方可就错拼的单词改成正确的单词
  • 根据人际网络找到关系最近的医生

图应用场景,要确定如何从双子峰前往金门大桥,需要两个步骤:

  • 使用图来建立问题模型
  • 使用广度优先搜索解决问题

图模拟一组连接。图由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。

你需要按添加顺序进行检查,有一个可实现这种目的的数据结构,那就是队列。

实现方法如下:

  • 队列的工作原理是先进先出,简单来说排队等公交车,先排的先上
  • 用散列表可以表示一个节点指定另外一个节点

其中A->B被称为有向图,其中的关系是单向的,箭头的方向指明了关系的方向。无向图没有箭头,直接相连的节点互为邻居,也被称为双向。
大O表示法为O(人数+边数),通常写作O(V+E)。

如果任务A依赖于任务B,在列表中任务A就必须在任务B后面。这被称为拓扑排序。

 

7、狄克斯特拉算法

寻找最短路径时,前面的BFS(广度搜索)算法每段时间是一样的,如果加上时间,用狄克斯特拉算法,你将发现更快(总权重最小)的路径。

狄克斯特拉算法步骤:

  • 找出最短时间内(开销最小)到达的节点
  • 更新该节点的邻居的开销
  • 重复这个过程,直到对图中的每个节点都这样做了。
  • 计算最终路径

带权重的图为加权图,不带权重的图为非加权图。狄克斯特拉算法只适用于有向无环图,而且不能有负权边。

负权变可使用贝尔曼-福德算法

使用三个散列表,一个散列表存储一个节点映射到它所有邻居。一个散列表存储这些边的权重,一个散列表存储它的开销。

8、贪婪算法

解决选课问题,尽可能上最多的课:

  • 选出结束最早的课
  • 接下来,必须选择第一堂课结束才开始的课

贪婪算法的核心是每步都选择局部最优解。并非适用于任务情况,但易于实现。

集合覆盖问题,尽可能覆盖全州:

  • 选出覆盖这样覆盖最多的未覆盖州,即使有些已经覆盖
  • 重复第一步,直到覆盖了所有的州

贪婪算法是一种近似算法。在获得精确解需要的时间太长时,可使用近似算法。判断近似算法优劣的标准如下:

  • 速度有多快
  • 得到的近似解与最优解的接近程度

使用集合表示要覆盖的州,但集中不能包含重复的元素。还需要可供选择的广播台清单,使用散列表表示。

集合是一种数据结构,类似列表。比如包含西红柿、香蕉等等水果集合,集合不能含有相同元素,并集是将集合合并。交集是找出两个集合中都有的元素,差集是从一个集合剔除另一个集合的元素。

NP完全问题:你需要计算所有的解,并从中选出最小/最短的那个。以难解著称,很多人认为不可能编写出可快速解决这些问题的算法。

NP完全问题特征:

  • 元素较少时运行速度非常快,随着元素数量的增加,速度会变得非常慢
  • 涉及“所有组合”的问题通常是NP完全问题
  • 不可能将问题分成小问题,必须考虑各种可能的情况
  • 如果问题涉及序列,且难以解决,比如旅行商问题中的城市序列。
  • 如果问题涉及集合(如广播台集合)且难以解决
  • 如果问题可转换为集合覆盖问题或旅行商问题

贪婪算法寻找局部最优解,企图以这种方式获得全局最优解。对于NP完全问题,还没有找到快速解决方案,面临NP完全问题时,最佳的做法是使用近似算法,贪婪算法易于实现,运行速度快,是不错的近似问题。

9、动态规划

      动态规划,是一种解决棘手问题的方法,它将问题分成小问题,并先着手解决这些小问题。原理就是:先解决小问题,再解决大问题。

      简单算法:尝试各种可能的商品组合,并找出价值最高的组合。这个算法很慢。

      网格可解决,用公式计算每个单元格的价值,每次迭代,最大价值不可能比以前低,不随排列变化,每次都计算最大价值。由于重量编号,你需要考虑的粒度更细,因此需要调整网格。

      但仅当每个子问题都是离散的,即不依赖其他子问题时,动态规划才管用。

  • 动态规划可帮助你再给定约束条件下找到最优解。
  • 在问题可分解为彼此独立且离散的子问题时,可用动态规划来解决。
  • 每个动态规划解决方案涉及到网络
  • 单元格的值通常就是你要优化的值
  • 每个单元格都是子问题,因此应考虑如何将问题分成子问题,这有助于你找出网络的坐标轴。

你管理一个网站,用户拼错了,你必须猜测他原本要输入的是什么单词。就需要求解最长公共子串。把字符串拆成一个字符,一个字符。

最长公共序列,判断相似性。编辑距离指出了字符串的相似程度。

10、K最近邻算法

    有某些特征进行分类。分类是边组,回归是预测结果。

     ①、创建推荐系统

  • 将用户放入一个图表中
  • 这些用户在图表的位置取决于其喜好、特征等
  • 假如你有A的特征,B的特征,A的特征和B比较像,有多像?你可以计算其相似性以及距离,距离为1时,你需要将每个用户都转换为一组坐标

     ②、回归

     回归是预测你打分,选你5个或多个相邻的特征的人算平均分。余弦相似度不计算两个矢量的距离,而是比较他们的角度。

     使用KNN时,挑选合适的特征进行比较至关重要。

  • 与推荐的电影紧密相关的特征
  • 不偏不倚的特征

 11、机器学习

      KNN在机器学习领域堪称领路人。

      OCR是指光学字符识别,计算机将自动识别出其中的文字,可实现图书数字化。

  • 浏览大量的数字图像,将这些数字特征提取出来(训练)
  • 遇到新图像时,你该提取图像的特征,再找出它最近的邻居
  • OCR算法提取线段、点和曲线等特征

     基于KNN等简单理念的,可用于语音识别和人脸识别。

     创建垃圾邮件过滤器,可用简单算法-朴素贝叶斯分类器

  • 你首先使用一些数据对于分类器进行训练
  • 可研究句子中每个单词,比如出现million在垃圾邮件的概率是多少
  • 朴素贝叶斯分类器能计算出邮件为垃圾邮件的概率

12、其他算法

        二分查找需要排序后才管用,如果能将用户名插入到数组的正确位置。这样无需在插入再排序。为此,有人设计了二叉查找树

        对于其中的每个节点。对于其中的每个节点,左节点的值都比它小,而右节点比它大。

       平均为O(logn),最糟为O(n)。虽然最糟情况比二分查找运行慢,但二分查找的插入和删除操作更快。它的缺点是不能随机访问。也有一些处于平衡的状态特殊二叉查找树-红黑树。

       B树就是一种特殊的二叉树,数据库常用它来存储数据。你对数据库或高级数据结构,请研究如下数据结构:B树、红黑树、堆、伸展树。

       这个散列表的健的单词,值为包含指定单词的页面。假设用户搜索Hi,这个情况下,搜索引擎需要检查那些页面包含Hi。这是一种很有种的数据结构:一个散列表,将单词映射的它的页面,这种数据结构被称为反向索引。常用于创建搜索引擎。

      傅里叶变换——给定一首歌曲,傅里叶变换能将其中的各种频率分离出来。非常适用于处理信号,可使用它压缩音乐。也可用于音乐识别软件。

     并行算法:提高算法速度,笔记本和台式机多采用多核处理器,为提高算法的速度,你需要让它们能够在多个内核并行地执行。算法速度限制于并行性管理开销和负载均衡。

     MapReduce:分布式算法,可通过流行的开源工具Apache Hadoop使用它。基于两个简单的函数:映射函数和归并函数

    布隆过滤器和HyperLogLog:一种概率型数据结构,为判断网页以前是否已搜集。使用时,答案却很可能是正确的。可能出现错报的情况,不可能出现漏报的情况。HyperLogLog是类似布隆过滤器的算法,近似算法(判断不完全对),计算用户执行的不同的搜索的数量。用户执行搜索时,google必须判断该搜索是否包含日志中,如果答案是否定,就加入日志中,即便只记录一天的搜索,这种日志大得不得了。

    SHA算法:安全散列函数,给定一个字符串,SHA返回其散列值。用于创建散列表的散列函数根据字符串生成数组索引,而SHA根据字符串生成另一个字符串。你可以用SHA判断两个文件是否相同,适用于超大型文件。检查密码,大多数网站存储的密码是SHA散列值。这种密码是单向的,即便获取SHA散列值,局部不敏感,也无法推断密码。

    Simhash:局部敏感的散列函数,可用于判断网页是否搜集,判断学生的论文是否是网上抄的,检查上传内容是否侵害版权。

    Diffie-Hellman密钥交换:加密算法,公钥和私钥,公钥是公开的,可发布到网上。有人向你发送消息时,使用公钥进行加密,加密的消息只有撕咬的界面,只有你知道私钥,解密消息。Diffie-Hellman算法及其替代者RSA依然被广泛使用。

    线性规划:用于在给定约束条件下最大限度地改善指定地指标。所有的图算法都可使用线性规划来实现,线性规划是一个宽泛得多的框架,图问题只是其中的一个子集。线性规划使用Simplex算法,这个算法很复杂。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python算法学习是指掌握使用Python语言来解决各种问题的方法和技巧。在学习过程中,有几个重要的方面需要注意。 首先,了解基本的算法概念是必不可少的。例如,了解常见的排序算法(如冒泡排序、快速排序)和查找算法(如二分查找)等。这有助于我们理解算法的原理和思想。 其次,学习Python中的内置数据结构和函数,如列表、字典和字符串等。熟悉这些数据结构和函数的使用方法,可以帮助我们更好地解决问题,并编写出高效的算法。 第三,了解常用的算法设计技巧。例如,贪心算法、动态规划和分治法等。这些算法设计技巧可以应用于不同类型的问题,帮助我们找到解决问题的最佳方法。 第四,编写和调试算法代码是学习过程中的关键步骤。我们可以通过编写小型的算法程序来练习和巩固所学的知识。同时,调试是解决代码错误和优化算法的重要环节,需要耐心和细心。 最后,不断练习和实践是提高算法能力的关键。我们可以通过刷题、参加编程比赛和项目实践等方式来提高自己的算法水平。与其他程序员交流和分享经验也是相互学习的好机会。 总之,Python算法学习是一个渐进的过程,需要不断学习、实践和提高。通过合理的学习方法和坚持不懈的努力,我们可以不断提高自己的算法能力,并运用它来解决实际问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值