python数据结构与算法分析(二)

本文探讨了Python中的数据结构与算法,包括递归、搜索、排序等主题。介绍了递归的三个原则,以及递归在实现汉诺塔问题中的应用。接着,讨论了动态规划、栈帧在实现递归中的作用,以及汉诺塔问题的解决方案。文章还深入分析了顺序搜索和二分搜索,指出在有序列表中,二分搜索的时间复杂度为O(logn)。最后,讲解了散列的概念,包括散列函数、冲突处理和散列表实现映射抽象数据类型的运用,强调了散列搜索算法在理想和冲突情况下的时间复杂度。
摘要由CSDN通过智能技术生成

数据结构与算法分析(一)

递归

递归是解决问题的一种方法,它将问题不断地分成更小的子问题,直到子问题可以用普通的方法解决。通常情况下,递归会使用一个不停调用自己的函数。

递归三原则:
(1) 递归算法必须有基本情况;
(2) 递归算法必须改变其状态并向基本情况靠近;
(3) 递归算法必须递归地调用自己。
基本情况是指使算法停止递归的条件,这通常是小到足以直接解决的问题。
为了遵守第二条原则,必须设法改变算法的状态,从而使其向基本情况靠近。改变状态是指修改算法所用的某些数据,这通常意味着代表问题的数据以某种方式变得更小。
最后一条原则是递归算法必须对自身进行调用,这正是递归的定义。
其实,递归的逻辑并不是循环,而是将问题分解成更小、更容易解决的子问题。

栈帧:实现递归
当调用函数时,Python分配一个栈帧来处理该函数的局部变量。当函数返回时,返回值就在栈的顶端,以供调用者访问。
栈帧限定了函数所用变量的作用域。尽管反复调用相同的函数,但是每一次调用都会为函数的局部变量创建新的作用域。

4.5汉诺塔
借助一根中间柱子,将高度为height的一叠盘子从起点柱子移到终点柱子:
(1) 借助终点柱子,将高度为height -1的一叠盘子移到中间柱子;
(2) 将最后一个盘子移到终点柱子;
(3) 借助起点柱子,将高度为height -1的一叠盘子从中间柱子移到终点柱子。
只要总是遵守大盘子不能叠在小盘子之上的规则,就可以递归地执行上述步骤,就像最下面的大盘子不存在一样。上述步骤仅缺少对基本情况的判断。最简单的汉诺塔只有一个盘子。在这种情况下,只需将这个盘子移到终点柱子即可,这就是基本情况。此外,上述步骤通过逐渐减小高度height来向基本情况靠近。
在这里插入图片描述
算法如此简洁的关键在于进行两个递归调用,分别在第3行和第5行。第3行将除了最后一个盘子以外的其他所有盘子从起点柱子移到中间柱子。第4行简单地将最后一个盘子移到终点柱子。第5行将之前的塔从中间柱子移到终点柱子,并将其放置在最大的盘子之上。基本情况是高度为0。此时,不需要做任何事情,因此moveTower函数直接返回。这样处理基本情况时需要记住,从moveTower返回才能调用moveDisk。
moveDisk函数非常简单,代码如下所示。它所做的就是打印出一条消息,说明将盘子从一根柱子移到另一根柱子。不妨尝试运行moveTower程序,你会发现它是非常高效的解决方案。
在这里插入图片描述

动态规划:
递归调用动态规划

搜索

搜索是指从元素集合中找到某个特定元素的算法过程。搜索过程通常返回True或False,分别表示元素是否存在。
Python提供了运算符in,通过它可以方便地检查元素是否在列表中。

①顺序搜索:
存储于列表等集合中的数据项彼此存在线性或顺序的关系,每个数据项的位置与其他数据项相关。在Python列表中,数据项的位置就是它的下标。因为下标是有序的,所以能够顺序访问,由此可以进行顺序搜索。
顺序搜索的原理:从列表中的第一个元素开始,沿着默认的顺序逐个查看,直到找到目标元素或者查完列表。如果查完列表后仍没有找到目标元素,则说明目标元素不在列表中。
顺序搜索算法的Python实现如下代码所示。这个函数接受列表与目标元素作为参数,并返回一个表示目标元素是否存在的布尔值。布尔型变量found的初始值为False,如果找到目标元素,就将它的值改为True。
在这里插入图片描述
在分析搜索算法之前,需要定义计算的基本单元,这是解决此类问题的第一步。对于搜索来说,统计比较次数是有意义的。每一次比较只有两个结果:要么找到目标元素,要么没有找到。这里做了一个假设,即元素的排列是无序的。也就是说,目标元素位于每个位置的可能性都一样大。
要确定目标元素不在列表中,唯一的方法就是将它与列表中的每个元素都比较一次。如果列表中有n个元素,那么顺序搜索要经过n次比较后才能确定目标元素不在列表中。如果列表包含目标元素,分析起来更复杂。实际上有3种可能情况,最好情况是目标元素位于列表的第一个位置,即只需比较一次;最坏情况是目标元素位于最后一个位置,即需要比较n次。
普通情况又如何呢?我们会在列表的中间位置处找到目标元素,即需要比较n/2次。当n变大时,系数会变得无足轻重,所以顺序搜索算法的时间复杂度是O(n)。下表总结了3种可能情况的比较次数。
在这里插入图片描述
前面假设列表中的元素是无序排列的,相互之间没有关联。如果元素有序排列,顺序搜索算法的效率会提高吗?
假设列表中的元素按升序排列。如果存在目标元素,那么它出现在n个位置中任意一个位置的可能性仍然一样大,因此比较次数与在无序列表中相同。不过,如果不存在目标元素,那么搜索效率就会提高。下图展示了算法搜索目标元素50的过程。注意,顺序搜索算法一路比较列表中的元素,直到遇到54。该元素蕴含额外的信息:54不仅不是目标元素,而且其后的元素也都不是,这是因为列表是有序的。因此,算法不需要搜完整个列表,比较完54之后便可以立即停止。代码展示了有序列表的顺序搜索函数。
在这里插入图片描述
在这里插入图片描述
下表总结了在有序列表中顺序搜索时的比较次数。在最好情况下,只需比较一次就能知道目标元素不在列表中。普通情况下,需要比较n/2次,不过算法的时间复杂度仍是O(n)。总之,只有当列表中不存在目标元素时,有序排列元素才会提高顺序搜索的效率。
在这里插入图片描述

②二分搜索
还可以改进算法,进一步利用列表有序这个有利条件。在顺序搜索时,如果第一个元素不是目标元素,最多还要比较n-1次。但二分搜索不是从第一个元素开始搜索列表,而是从中间的元素着手。如果这个元素就是目标元素,那就立即停止搜索;如果不是,则可以利用列表有序的特性,排除一半的元素。如果目标元素比中间的元素大,就可以直接排除列表的左半部分和中间的元素。这是因为,如果列表包含目标元素,它必定位于右半部分。
接下来,针对右半部分重复二分过程。从中间的元素着手,将其和目标元素比较。同理,要么直接找到目标元素,要么将右半部分一分为二,再次缩小搜索范围。下图展示了二分搜索算法如何快速地找到元素54,完整的函数如代码所示。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值