分享好友 最新动态首页 最新动态分类 切换频道
geeksforgeeks —— 算法 1
2024-12-27 03:28

geeksforgeeks —— 算法 1

geeksforgeeks 上有很多不错的基础性计算机学科知识,其风格不过多注重理论,也不是一味的像 leetcode 那种刷题,每一篇内容篇幅安排的都较短,也有一定的知识组织架构,非常适合初学者或作为工具字典书定向查阅相关内容。
该合集内容主要针对的是算法与数据结构方面相关内容,不做 100% 的翻译且基本是机翻,所有的代码只保留 Python 版本。

1.1 线性查找

  • 难度级别:基础
  • 更新时间:2021.02.09
  • source:https://www.geeksforgeeks.org/linear-search/

问题 给定一个具有 个元素的数组 arr[],写一个函数可以在数组 arr[] 中查找指定的元素

示例

 
 
 

Output

 

时间复杂度是 。

线性搜索很少被实际使用,因为其他搜索算法,如二分查找算法和哈希表比线性搜索更快。

1.2 二分查找

  • 难度级别:简单
  • 更新时间:2020.02.03
  • source:https://www.geeksforgeeks.org/binary-search/

给定一个由 个元素组成的已排序的数组 arr[],编写一个函数来搜索 arr[] 中的给定元素 。

一个简单的方法是做线性查找,其时间复杂度是 。另一个解决该问题的方式是使用二分查找。

二分查找 通过重复将搜索区间对半来搜索已排序的数组。从搜索整个数组的区间开始。如果搜索的值小于区间中间的项,则将区间缩小到下半部分。否则就缩小到上半部分。反复检查,直到找到值或区间为空。

我们基本可以在每一次比较后忽略掉一般的元素

  1. 将 与中间位置的元素进行比较
  2. 如果 与中间的元素匹配,则返回中间位置的索引
  3. 否则,如果 比中间位置元素还大,则 只能位于右半部分,所以我们将关注右半部分的元素
  4. 否则( 比较小)则关注左半部分的元素

递归实现二分查找

 

Output :

 

迭代实现的二分查找

 

Output :

 

时间复杂度

 

上述递推问题可用递推树法或 Master 方法求解。它属于 Master 方法的第二种情况,其递推解为 。

辅助空间 对于迭代方式是 ,对于递归方式,需要 的堆栈空间。

1.3 跳跃搜索

  • 难度级别:容易
  • 更新时间:2021.03.22
  • source: https://www.geeksforgeeks.org/jump-search/

与二分搜索一样,跳跃搜索也是一种排序数组的搜索算法。其基本思想是通过按固定的步骤向前跳或跳过某些元素而不是搜索所有元素来检查较少的元素(相对线性搜索)。

例如,假设我们有一个数组 arr[],大小为 ,块(要跳转)的大小为 。然后我们搜索索引 arr[0]、arr[m]、arr[2m]……arr[km] 等等。一旦我们找到区间(arr[km] < x < arr[(k+1)m],我们就从索引 km 执行线性搜索操作来找到元素 x。

考虑如下数组 (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610). 数组长度是 16,将利用跳跃搜索 55,假设跳跃的块大小为 4,则查找顺序如下

  1. 从索引 0 跳到索引 4
  2. 从索引 4 跳到索引 8
  3. 从索引 8 跳到索引 12
  4. 因为索引 12 的值比 55 大,我们将回跳到索引 8
  5. 从索引 8 开始执行线性查找,找到元素 55

最佳的跳跃块大小是多少

在最坏的情况下,我们必须进行 次跳转,如果最后检查的值大于要搜索的元素,我们将执行 比较以进行线性搜索。因此,最坏情况下的比较总数为)。当 时取得最小值,因此,最佳步长为 .

 

Output:

 

时间复杂度,辅助空间

重要点

  • 只能用于已被排序的数组中
  • 最优的块大小为 ,这可以使跳跃查找的复杂度为
  • 跳跃查找的时间复杂度介于线性查找() 和二分查找(O()) 之间
  • 二分搜索比跳跃搜索好,但是跳跃搜索有一个优点,是我们仅回溯一次(二分搜索最多可能需要 个跳转,请考虑以下情况:要搜索的元素是最小的元素或小于最小的元素)。因此,在二分搜索成本很高的系统中,我们使用跳跃搜索。

References:
https://en.wikipedia.org/wiki/Jump_search

1.4 插值搜索

  • 难度级别:容易
  • 更新时间:2021.03.22
  • source: https://www.geeksforgeeks.org/interpolation-search/

给定一个由 个均匀分布的值组成的排序数组 arr[],编写一个函数来搜索数组中的特定元素 。

线性搜索的复杂度是 ,跳跃搜索的复杂度是 ,二分搜索的复杂度是 。

对于排序数组中的值是均匀分布的,则插值搜索是对分搜索的改进。二分搜索总是转到中间元素进行检查。而插值搜索可以根据正在搜索的 key 的值去到不同的位置。例如,如果 key 的值更接近最后一个元素,则插值搜索可能会朝着结束侧开始搜索。

为了找到要搜索的位置,它使用以下公式

 

pos的公式可以推导如下

 

算法

  1. 在循环中,使用探头位置公式计算 “pos” 的值。
  2. 如果是匹配项,则返回该项的索引,然后退出。
  3. 如果该项小于 arr[pos],则计算左侧子数组中的探头位置。否则在右边的子数组中计算相同的值。
  4. 重复此操作,直到找到匹配项或子数组减少到零。

下面是该算法的实现

 

Output

 

1.5 指数搜索

  • 难度级别:容易
  • 更新时间:2021.03.24
  • source:https://www.geeksforgeeks.org/exponential-search/

此搜索算法的名称可能会产生误导,因为它在 时间内完成搜索。名称来自它搜索元素的方式。

 

指数搜索有两个步骤

  1. 查找元素所在的范围
  2. 在上述范围内进行二分搜索。

怎么查找元素可能的所在范围

这个想法是从子数组大小为 1 开始,将其最后一个元素与 进行比较,然后尝试大小 2,然后 4,依此类推,直到子数组的最后一个元素不大于 。

一旦我们找到一个索引 ,我们就知道元素必须存在于 和 之间(为什么 ?因为我们在上一次迭代中找不到更大的值,下面给出了上述步骤的实现

 

Output

 

时间复杂度

辅助空间: 上述二分搜索的实现是递归的,需要 空间。使用迭代二进制搜索,我们只需要 空间。

指数搜索的应用

  1. 指数二分搜索对于数组大小无限的无界搜索特别有用。有关示例,请参阅无界二分搜索。
  2. 对于有界数组,以及当要搜索的元素更接近第一个元素时,它比二分搜索效果更好。

Reference:
https://en.wikipedia.org/wiki/Exponential_search

1.6 为什么二元搜索优于三元搜索

  • 难度级别:中等
  • 更新时间:2019.10.01
  • source:https://www.geeksforgeeks.org/binary-search-preferred-ternary-search/

下面是一个简单的递归实现的三元搜索函数

 

在最坏的情况下,二分和三分方法中哪一种比较少

三元搜索在进行 递归调用时,比较的次数似乎较少,但是二元搜索进行 递归调用。让我们仔细看看。

以下是在二分搜索的最坏情况下计算比较的递归公式。

 

以下是三元搜索最坏情况下计数比较的递归公式。

 

在二分搜索中,最坏情况下有 比较。在三元搜索中,最坏情况下有 次比较。

 

因此,三元和二元搜索的比较归结为表达式 和 的比较。 的值可以写成。由于 的值大于 1,在最坏的情况下,三元搜索比二元搜索做更多次的比较。

练习

为什么归并排序将输入数组分成两半,为什么不分成三部分或更多部分


1.7 选择排序

  • 难度级别:容易
  • 更新时间:2019.05.02
  • source:https://www.geeksforgeeks.org/selection-sort/

选择排序算法通过从未排序的部分重复寻找最小元素(考虑升序)并将其放在开始处来对数组进行排序。该算法在给定的数组中保持两个子数组。

  • 已排序的子数组
  • 未排序的剩余子数组

在选择排序的每次迭代中,从未排序的子数组中选取最小元素(考虑升序,并将其移动到已排序的子数组中。

以下示例说明了上述步骤

 
 

Output:

 

时间复杂度 ,因为有两个嵌套循环。

辅助空间 选择排序的好处在于,它不会进行超过 次的交换,而且在内存写入是一项代价高昂的操作时非常有用。


1.8 冒泡排序

  • 难度级别:容易
  • 更新时间:2021.05.07
  • source:https://www.geeksforgeeks.org/bubble-sort/

冒泡排序是最简单的排序算法,如果相邻元素顺序错误,它会重复交换相邻元素。

示例

第一轮

( 5 1 4 2 8 ) –> ( 1 5 4 2 8 ),在这里,算法比较前两个元素,因为 5 > 1,交换
( 1 5 4 2 8 ) –> ( 1 4 5 2 8 ),因为 5 > 4,交换
( 1 4 5 2 8 ) –> ( 1 4 2 5 8 ),因为 5 > 2,交换
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 ),因为 5 < 8,不做交换

第二轮

( 1 4 2 5 8 ) –> ( 1 4 2 5 8 )
( 1 4 2 5 8 ) –> ( 1 2 4 5 8 ),因为 4 > 2,交换
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )

现在,数组已经排序,但是我们的算法不知道它是否完成。该算法需要一个完整的没有任何交换的过程,它才能确定排序结束。

第三轮

( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )

下面是冒泡排序的实现

 

Output:

 

优化实现

即使对已排序的数组进行排序,上述函数也始终运行 次。如果内环没有任何交换,可以停止。

 

Output:

 

最差和平均时间复杂度 ,当数据逆序排序时是最坏情况

最好情况下时间复杂度 ,即数组已经是排序好的情况下

辅助空间

属于就地排序、稳定排序

由于其简单性,冒泡排序经常被用来引入排序算法的概念。在计算机图形学中,它很受欢迎,因为它能够在几乎已排序的数组中检测到非常小的错误(如两个元素的交换,并以线性复杂度)修复它。例如,它用于多边形填充算法,其中边界线在特定扫描线(平行于 轴的线)处按其 坐标排序,随着 的增加,它们的顺序变化(两个元素交换)仅在两条线的交点处(来源:Wikipedia


1.9 插入排序

  • 难度级别:容易
  • 更新时间:2021.04.13
  • source:https://www.geeksforgeeks.org/insertion-sort/

插入排序是一种简单的排序算法,其工作原理类似于对手中的扑克牌进行排序。数组实际上分为排序部分和未排序部分。未排序数组中的值将被拾取并放置在已排序数组中的正确位置。

算法

要按升序对大小为 的数组排序

  1. 在数组上从 arr[1] 迭代到 arr[n]
  2. 将当前元素(key)与其前一个元素进行比较
  3. 如果 key 元素比前一个元素小,与前一个元素进行比较。将较大的元素向上移动一个位置,为交换的元素腾出空间。
 

时间复杂度

辅助空间

稳定排序、就地排序、在线排序

使用场景 当元素数较少时使用插入排序。当输入数组几乎被排序时,它也很有用。

什么是二分插入排序

我们可以使用二分搜索来减少正常插入排序中的比较次数。二分插入排序使用二分搜索来查找在每次迭代中插入选定项的正确位置。在正常插入中,排序在最坏情况下取 (在第 次迭代中)。我们可以使用二分搜索将其缩减为 。由于每次插入都需要一系列的交换,因此该算法总体上仍具有 的最坏运行时间。具体实现参阅这里。

如何实现链表的插入排序

 

具体实现参照这里。


1.10 归并排序

  • 难度级别:中等
  • 更新时间:2021.05.18
  • source:https://www.geeksforgeeks.org/merge-sort/

与快速排序一样,归并排序也是一种分而治之的算法。它将输入数组分成两半分别排序,然后合并已排序的两半。merge() 函数用于合并。 是一个关键过程,它假定 和 被排序,并将两个排序的子数组合并为一个。

 

下图显示了示例数组 {38,27,43,3,9,82,10} 的完整合并排序过程,示例来自于 wikipedia。如果我们仔细看这个图,我们可以看到数组被递归地分成两半,直到大小变为 1。一旦大小变为 1,合并过程就开始了,直到整个数组被合并。

 

Output

 

时间复杂度 归并排序是一种递归算法,时间复杂度可以表示为以下递推关系

上述递推可采用递推树法或 Master 方法求解。它属于 Master 方法的第二种情况,其递推解是 。归并排序的时间复杂度在所有 3 种情况下(最差、平均和最佳)均为 ,归并排序总是将数组分为两半,并且需要线性时间来合并两半。

辅助空间

典型的实现并非就地排序,属于稳定排序

归并排序的应用

  1. 归并排序对于在 时间内对链表进行排序非常有用。在链表下,情况不同主要是因为数组和链接列表的内存分配不同。与数组不同,链接的列表节点可能在内存中不相邻。与数组不同,在链接列表中,我们可以在 额外空间和 时间内完成插入。因此,归并排序的合并操作可以不需要为链表留出额外空间。在数组中,我们可以进行随机访问,因为元素在内存中是连续的。假设我们有一个整数(4 字节)数组 A,让 A[0] 的地址为 ,那么要访问A[i],我们可以直接访问)处的内存。与数组不同,我们不能在链表中进行随机访问。快速排序需要大量此类访问。在链表中,为了访问第 个索引,我们必须将每个节点从头部移动到第 个节点,因为我们没有连续的内存块。因此,快速排序的开销会增加。归并排序按顺序存取数据,对随机存取的要求较低。
  2. 倒数问题
  3. 用于外部排序

归并排序的缺点

  • 对于较小的任务,与其他排序算法相比速度较慢。
  • 归并排序算法需要为临时数组增加 的内存空间。
  • 即使对已排序数组进行排序,它也会贯穿整个过程。

1.11 堆排序

  • 难度级别:中等
  • 更新时间:2021.05.18
  • source:https://www.geeksforgeeks.org/heap-sort/

堆排序是一种基于二叉堆数据结构的比较排序技术。它类似于选择排序,我们首先找到最小元素并将最小元素放在开头。我们对其余的元素重复同样的过程。

什么是二叉堆

让我们先定义一个完整的二叉树,在这个二叉树中,除了可能的最后一层外,每一层都被完全填满,所有的节点都尽可能的左移(来源维基百科

二叉堆是一个完整的二叉树,其中项目按特殊顺序存储,使得父节点中的值大于(或小于)其两个子节点中的值。前者称为最大堆,后者称为最小堆。堆可以用二叉树或数组表示。

为什么要对二叉堆使用基于数组的表示

由于二叉堆是一个完整的二叉树,因此它可以很容易地表示为一个数组,并且基于数组的表示是空间有效的。如果父节点存储在索引 处,则左子节点可以按计算,右子节点可以按 计算(假设索引从 0 开始)。

按递增顺序排序的堆排序算法

  1. 从输入数据构建最大堆。
  2. 此时,最大的项存储在堆的根目录下。用堆的最后一项替换它,然后将堆的大小减少 1。最后,堆化树的根。
  3. 当堆的大小大于 1 时,重复步骤 2。

如何构建堆

Heapify 过程仅当其子节点被 Heapify 时才能应用于节点。因此,堆化必须按自下而上的顺序进行。

通过一个例子让我们了解

 
 

Output

 

就地排序,典型实现是非稳定的,稳定版可以参照这里

时间复杂度 heapify 的时间复杂度为 。createAndBuildHeap() 的时间复杂度为 ,堆排序的总体时间复杂度为 。

HeapSor t的应用

  1. 对近似排序(或K排序)数组进行排序
  2. 求 top k

堆排序算法的用途有限,因为快速排序和归并排序在实践中效果更好。然而,堆数据结构本身被大量使用。


1.12 快速排序

  • 难度级别:中等
  • 更新时间:2021.04.28
  • source:https://www.geeksforgeeks.org/quick-sort/

与归并排序一样,快速排序也是一种分而治之的算法。它选取一个元素作为轴,并围绕所选取的轴对给定数组进行分区。有许多不同版本的快速排序以不同的方式选择轴。

  1. 始终选择第一个元素作为轴。

  2. 始终选择最后一个元素作为枢轴(在下面实现

  3. 选择一个随机元素作为轴心。

  4. 选择中间带作为轴。

快速排序的关键过程是 partition()。分区的目标是,给定一个数组和数组的一个元素 作为轴心,将 放在排序数组中正确的位置,将所有较小的元素(小于)放在 之前,将所有较大的元素(大于 )放在 之后。所有这些都应该在线性时间内完成。

快速排序的递归版本伪代码

 
 

Partition 算法

划分的方法有很多种,下面的伪代码采用 CLRS 手册中给出的方法。逻辑很简单,我们从最左边的元素开始,跟踪较小(或等于)元素的索引作为 。在遍历时,如果我们找到一个较小的元素,我们就用 arr[i] 交换当前元素。否则我们忽略当前元素。

 

Pseudo code for partition()

 

partition() 图解 :

 
 

Output

 

时间复杂度分析 最好情况下,最好和平均情况下都是 ,具体分析过程略。

尽管快速排序的最坏情况时间复杂度是 ,这比许多其他排序算法(如归并排序和堆排序)都要高,但是快速排序在实践中速度更快,因为它的内部循环可以在大多数体系结构和大多数实际数据中有效地实现。快速排序可以通过改变 pivot 的选择以不同的方式实现,这样对于给定的数据类型,最坏的情况很少发生。然而,当数据很大且存储在外部存储器中时,归并排序通常被认为更好。

默认实现不稳定。然而,任何排序算法都可以通过将索引作为比较参数来实现稳定。根据就地算法的广泛定义,它是就地排序算法,因为它只使用额外的空间来存储递归函数调用,而不用于操作输入。

什么是三向快速排序

在简单的快速排序算法中,我们选择一个元素作为 pivot,围绕 pivot 对数组进行划分,并在 pivot 的左右两侧对子数组进行递归。

考虑一个有许多冗余元素的数组。例如,{1,4,2,4,2,4,1,2,4,1,2,2,2,2,4,1,4,4,4}。如果在简单快速排序中选择 4 作为轴心,我们只修复一个 4 并递归处理剩余的事件。在三向快速排序中,数组 arr[l…r] 被分为三部分

  • arr[l…i] 元素小于枢轴。
  • arr[i+1…j-1] 元素等于枢轴。
  • arr[j…r] 元素大于枢轴。

关于快速排序在链表上的实现以及迭代方式实现可以参考

  • QuickSort on Singly Linked List

  • QuickSort on Doubly Linked List

  • Iterative Quick Sort.

为什么快速排序比归并排序更适合排序数组

快速排序的一般形式是就地排序(即它不需要任何额外的存储,而归并排序需要 额外的存储, 表示数组大小,这可能非常昂贵。分配和取消分配用于归并排序的额外空间会增加算法的运行时间。比较平均复杂度,我们发现这两种类型的排序都有 平均复杂度,但常数不同。对于数组,由于使用了额外的 存储空间,归并排序较差。

快速排序的大多数实际实现都使用随机版本。随机版本的时间复杂度预期为 。最坏的情况在随机化版本中也是可能的,但最坏的情况不会出现在特定的模式(如排序数组)中,随机化快速排序在实践中效果很好。

快速排序也是一种缓存友好的排序算法,因为它在用于数组时具有良好的引用局部性。

快速排序也是尾部递归的,因此尾部调用优化已经完成。

为什么对于链表来说 MergeSort 比 QuickSort 更受欢迎

在链表的情况下,情况不同主要是由于数组和链表的内存分配不同。与数组不同,链表节点在内存中可能不相邻。与数组不同的是,在链表中,我们可以用 额外的空间和 时间内完成插入。因此,归并排序的合并操作可以在不为链表增加额外空间的情况下实现。

在数组中,我们可以进行随机存取,因为元素在内存中是连续的。假设我们有一个整数(4 字节)数组 A,让 A[0] 的地址为 ,那么要访问 A[i],我们可以直接访问)处的内存。与数组不同,我们不能在链表中进行随机访问。快速排序需要大量此类访问。在链表中,为了访问第 个索引,我们必须将每个节点从头部移动到第 个节点,因为我们没有连续的内存块。因此,快速排序的开销会增加。归并排序按顺序存取数据,对随机存取的要求较低。

如何优化快速排序,以便在最坏的情况下占用 额外的空间

最新文章
镇江刀片产品表面视觉检测方案设计实时反馈全+境+到+达
  镇江刀片产品表面视觉检测方案设计实时反馈全+境+到+达——苏州希佑科技有限公司!  提供:  计算机视觉|人工智能检测|人工智能视觉检测|CCD 视觉检测|视觉应用|视觉深度学习|AI人工智能检测|AI人工智能图像处理|AI图像处理|视觉检
SEO常见术语分析,助你掌握搜索引擎优化之路
随着互联网的快速发展,搜索引擎优化(SEO)已成为企业提升品牌知名度、拓展市场份额的重要手段。为了帮助大家更好地理解SEO,本文将解析一些常见的SEO术语,助你掌握搜索引擎优化之道。一、(Keywords)关键词是SEO的核心,指的是用户在搜
魔兽世界隔墙有耳任务攻略与完成技巧解析
在《魔兽世界》中,隔墙有耳是一项颇具挑战性的任务,它要求玩家在特定的地点 stealth 潜行,并收集情报以了解敌人的阴谋。这项任务不仅考验玩家的潜行技巧,还考验其策略思考能力和对环境的观察力。接下来,我们将分享一些完成该任务的实
谷歌推广新手教程【谷歌推广app】
本文目录导读:谷歌推广的基本概念谷歌推广的账号设置谷歌推广的广告类型谷歌推广的关键词研究谷歌推广的广告创意谷歌推广的投放设置谷歌推广的效果评估与优化在当今数字化的时代,谷歌推【浙江seo】广已成为企业和个人进行网络营销的重要
快递物流1月7日,一月七日快递停运吗
1、极兔快递物流不动是什么原因2、跨越速运2023年春节不打烊政策3、2021河北石家庄快递什么时候恢复4、...是PA开头的单号,只显示1月7日在福州,到今天都没有更新了!!!_百度知...1、物流公司没有更新网页信息:由于物流公司每日揽件量和运输
学习seo课程的费用(seo的培训课程学费)
大家好,今天小编关注到一个比较有意思的话题,就是关于学习seo课程的费用的问题,于是小编就整理了5个相关介绍学习seo课程的费用的解答,让我们一起看看吧。网站seo优化多少钱?seo外贸推广费用多少?seo优化推广多少钱?关键词优化按天收
歼八最新型崛起,军事科技尖端力量的探索
摘要:歼八最新型的崛起,代表着军事科技的尖端力量。这款战机以其卓越的性能和先进的技术,展示了中国在军事领域的实力和进步。通过不断的研究和创新,歼八最新型战机已成为中国军事力量的重要支柱,为维护国家安全提供了强有力的支持。本
谷歌(GOOGL.US)搜索涉嫌垄断 苹果(AAPL.US)高管将出庭为其辩护
智通财经APP获悉,据知情人士透露,苹果(AAPL.US)服务部门主管定于当地时间周二在华盛顿作证,计划为其与谷歌(GOOGL.US)的协议进行辩护,称谷歌搜索引擎成为iPhone的默认选项是消费者的最佳选择。苹果负责服务的高级副总裁、该协议的设计者
提升外链实力,下载免费外链工具软件368
外链是搜索引擎优化 (SEO) 的关键因素,有助于提高网站的知名度、信任度和排名。为了帮助网站管理员和 SEO 专业人员轻松有效地建立外链,本文提供了全面的网站外链建设规划计划和执行方案,并精心挑选了免费且强大的外链工具软件下载。外链
马斯克并非狗狗唯一支点,蚂蚁L9 来特DOGE性能王者
备受政客支持的狗狗币的当前价格为0.42728美元,24小时内的涨幅为1.81%,其未来走势是被看好的。自美国选举日以来,狗狗币的价格已经上涨了惊人的153%,比特币在同一时期也上涨了30%。因为狗狗币等数字货币在短期内取得了显著的涨幅,所以
相关文章
推荐文章
发表评论
0评