背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读七个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》。
当前阅读周书籍:《秒懂算法:用常识解读数据结构与算法》。
算法为何重要
算法就是完成特定任务所需的一组操作。
有序数组
有序数组和第1章中的“传统”数组几乎完全一致。它们唯一的区别在于有序数组中的值是按顺序排列的。也就是说,插入新值时,这个值必须被放到一个合适的格子中,以免打乱数组的顺序。
以数组[3, 17, 80, 202]为例,假设要插入值75。如果这是一个传统数组,那么可以像下图这样在末尾插入75。但如果这是有序数组,那么别无选择,只能把75插入合适的位置,以保证数组的值是递增的,下面来一步步分析这个过程。
第1步:检查索引0处的值来确定要在它前面还是后面插入新值75。因为75比3大,所以必须插到它右边。不过,因为依然不知道具体位置,所以必须检查下一个格子。这样的步骤叫作比较。我们会比较要插入的值和有序数组中现有的值。
第2步:检查下一个格子的值。75比17大,所以还得继续右移。
第3步:检查下一个格子的值。这次的值是80,比要插入的75大。因为我们碰到了第一个比75大的值,所以得出结论:要保证有序数组的有序性,75必须紧挨着放在80的左边。为此,需要右移数据为75腾出空间。
第4步:把最后一个值右移。
第5步:把倒数第二个值右移。
第6步:把75插到正确的位置。
在这个例子中,数组有4个元素,插入用了6步。而对于包含N个元素的有序数组,插入则需要花N+2步。
有序数组的查找
假设我们有一个常规数组[17, 3, 75, 202, 80]。如果要查找一个数组中不存在的值22,则需要检查每个元素,因为22有可能在数组中的任何位置。除非在检查到数组末尾之前就找到这个值,否则只能全部检查一遍。
而对有序数组来说,即便值不在数组中,也能提前结束查找。假设要在有序数组[3, 17,75, 80, 202]中查找值22,因为22不可能在75右边,所以只需检查到75即可。
有序数组线性查找的Ruby实现如下:
这种方法有两个参数:array是查找的有序数组,search_value是要查找的值。
要在上面的范例数组中查找22,可以像下面这样调用上面的函数:
如你所见,linear_search方法在寻找search_value时需要遍历数组的每一个元素。当遍历到的element大于search_value时,查找立刻结束。这是因为剩下的数组中不可能含有search_value。
二分查找
下面来看看如何将二分查找应用于有序数组。假如有序数组有9个元素。假设我们想在这个有序数组中查找7。二分查找的过程如下:
第1步:从中间的格子开始查找。因为可以用数组长度除以2来计算其索引,所以可以立刻跳转到这个格子,然后检查其中的值。因为值是9,所以我们知道7在它的左边。这样就排除了9右边的一半的格子(包括它自己)。
第2步:在9左边的格子中,检查最中间的值。因为中间有两个值,所以可以随机选择其中一个,这里以左边的值为例,这个值是4,7一定在它的右边。我们可以排除4及其左边的格子。
第3步:现在还有两个可能是7的格子。随机选择两个格子中左边的那个。
第4步:检查最后一个格子。找到7用了4步。
代码实现:二分查找
以下是二分查找的Ruby实现:
binary_search方法同样以array和search_value为参数。可以像下面这样调用这个方法:
二分查找与线性查找
对于比较小的有序数组,二分查找相对于线性查找的优势并不大。
对线性查找来说,有多少个元素就有多少步。每次加倍数据量,查找的步骤数也要加倍。而二分查找在加倍数据量时,只需要多1步。
有序数组的插入更慢,而查找更快。
总结
要达成一个计算目标通常有多种方法,而你选择的算法可能会严重影响代码的速度。
还有一点很重要:通常没有完美适用于任何情况的数据结构或者算法。例如,有序数组可以用二分查找,但这并不意味着就应该一直使用它。在某些不太需要查找数据而只需插入数据的场合,因为插入操作更迅速,所以传统数组可能更合适。
分析算法的方法就是计算其需要的步骤数。
作者介绍非职业「传道授业解惑」的开发者叶一一。《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。