排序算法时间复杂度
![](https://i-blog.csdnimg.cn/blog_migrate/405ce215f6d457e92efcda313797f273.png)
当您听到“排序算法”时。 想到什么? 快速排序? 合并排序? 计数排序? 气泡排序? 哈利·波特(Harry Potter)的分拣帽及其将学生放在房子里的算法?
撇开所有笑话,我认为识别这些算法很重要,因为好吧,如果不进行排序,我们会在哪里? 毕竟,为了使用二进制搜索,我们必须对元素进行排序。 我们将如何查找字典中的单词或电话簿中的名称? 由于排序,我们日常的许多日常工作要简单得多。
通过排序,我们的意思是将元素按特定顺序放入列表中。 可以是数字顺序或字典顺序 (字母顺序)。
即使大多数编程语言都提供了一些开箱即用的排序功能,也最好退后一步,将它们分开,这样我们才能真正了解幕后情况。 在本周的博客中,我们将使用JavaScript中的各种排序算法探索幕后情况。 我们将从性能较低的算法开始,然后逐步发展为性能较高的算法。
气泡排序
时间复杂度-
最佳情况: Ω (n)
平均情况: Θ (n²)
最坏的情况:O(n²)
空间复杂度-
最坏的情况:O(1)
早在2007年竞选期间,奥巴马曾访问Google,并在一次问答环节中与当时的首席执行官埃里克·施密特(Eric Schmidt)进行了很少的交流。
Eric Schmidt :对一百万个32位整数进行排序的最有效方法是什么?
奥巴马总统 :嗯...
埃里克·施密特(Eric Schmidt) :也许-对不起...
奥巴马总统 :不,不,不,不。 我认为-我认为泡沫排序将是错误的方法。
埃里克·施密特 ( Eric Schmidt) :[facepalm]加油。 谁告诉他的? 好的。 在您的背景中我看不到计算机科学。
奥巴马总统 :我们那里有间谍。
冒泡排序并不是最大的选择,显然甚至在采访前奥巴马也对此进行了介绍。 之所以不是这样,是因为我们现在有许多更有效的替代解决方案来处理排序。
让我们看一下代码!
![](https://i-blog.csdnimg.cn/blog_migrate/39413c2e79d366fd0435c8673045369f.png)
如果您根本不熟悉气泡排序或忘记了它的工作方式,建议您在线搜索它的可视化图像。 当我了解排序时,可视化步骤有助于我理解它们。
冒泡排序的工作方式是,对于要迭代的每个元素,我们要检查索引中的下一个元素是否小于它。 如果是这样,我们将实质上交换元素。 在这两种实现中,您都可以看到我正在使用temp变量作为占位符,以便可以正确切换数组中的元素。
在第二个实现中,我使用的是while循环,当交换为true时运行。 在迭代开始时,我们立即将swapd设置为false。 如果我们检测到需要交换的元素,则将swapd设置为true并继续交换元素。
选择排序
时间复杂度-
最佳情况: Ω (n²)
平均情况: Θ (n²)
最坏的情况:O(n²)
空间复杂度-
最坏的情况:O(1)
选择排序以其简单性而闻名。 像冒泡排序一样,对于大型列表或数组,选择排序效率很低。 但是,由于空间复杂度是恒定的,因此它仍然有一些应用程序,并且还没有完全陷入模糊。
![](https://i-blog.csdnimg.cn/blog_migrate/2bda50002c3af2ee4a50a2e265dee9cf.png)
在选择排序的实现中,您可以看到我们有一个辅助函数minAndRemove负责从数组中查找最小元素,然后从数组中删除该元素,然后将其返回。
我们正在慢慢削减给定的数组。 每次找到最小元素时,我们都会注意到该元素,使用拼接缩短数组,然后将标注的元素推入新数组。 我们将继续执行此操作,直到不再有其他元素为止。 我们的新数组保证可以排序。
合并排序
时间复杂度-
最佳情况: Ω (n log(n))
平均情况: Θ (n log(n))
最坏的情况:O(n log(n))
空间复杂度-
最坏的情况:O(n)
合并排序时速度相当稳定。 但是,本伯叔叔(Spiderman参考资料)曾经说过“……时间复杂度很高,而空间复杂度却不那么高”。 我只是在开玩笑,本叔叔从没这么说过。
![](https://i-blog.csdnimg.cn/blog_migrate/6b5ca0ac2f7ebdda4f2fec5b27dea792.png)
合并排序背后的想法很有趣。 我们实质上是将这种分类工作分为较小的子问题。 每当我听到“子问题”一词时,我都会立即考虑使用递归。 我们将采用“分而治之”风格的算法。 这是使用递归的好地方。
在实现合并排序时,我们要做的第一件事是将数组分成两半。 然后,我们希望对这两半进行排序。 然后将这两个半部分再分成两个半部分。 我们的main函数会将数组拆分成两半,然后将两个半部分作为参数发送给我们的helper函数,然后helper函数将每个元素与每个元素进行一个比较,并返回一个新的排序数组。 主合并功能将使用递归将数组切成两半,直到遇到基本情况为止。 然后,我们将两个半部分各具有一个元素,然后开始建立排序数组。 随着递归调用栈的逐步展开,我们开始向新数组中添加越来越多的元素,直到…瞧! 排序!
快速排序
时间复杂度-
最佳情况: Ω (n log(n))
平均情况: Θ (n log(n))
最坏的情况:O(n²)
空间复杂度-
最坏的情况:O(n log(n))
许多标准库都内置有排序功能。Ruby和JavaScript都开箱即用。 在后台,这些功能正在使用快速排序算法。
快速排序的很棒之处在于它只占用很少的空间,而且速度非常快。 当然,最坏的情况可能会花费二次时间,但是在大多数情况下,这是一种高效的算法。
让我们看一下一个实现。
![](https://i-blog.csdnimg.cn/blog_migrate/969c2d491460e6284fce9af0ccb76540.png)
与合并排序的实现类似,我们的快速排序的实现将采用“分而治之”。 除此之外,我们将利用“枢轴”。 枢轴是确定将数字放置在较小数组还是较大数组中的决定因素。 拆分数组后,我们将在较大的数组上递归调用quickSort。 由于我们将这个问题分为子问题,因此我们可以使用递归来帮助我们。
计数排序
时间复杂度-
最佳情况: Ω (n + k)
平均情况: Θ (n + k)
最坏的情况:O(n + k)
空间复杂度-
最坏的情况:O(k)
计数排序是一种非常快速且节省空间的算法。 但实际上仅适用于对数字进行排序。 完全避免像我们讨论的所有算法那样进行任何类型的比较。
使用的技术基于特定范围之间的键。 它通过计算具有不同键值(类似于散列)的对象的数量来工作。 然后做一些算术计算每个对象在输出序列中的位置。
![](https://i-blog.csdnimg.cn/blog_migrate/3d8d196432a2a74084f774504710c055.png)
让我们逐步执行此实现的步骤。 我们需要在进入之前知道最大数量。
第1步-分配一个数组numCounts,其中的索引表示我们数组中的数字,而值表示该数字出现的次数。 每个值都从零开始。
步骤2 –在输入数组的一遍中,随着我们的更新numCount。
步骤3 —分配另一个名为sortedArray的数组,一旦元素排序,我们将在其中存储元素
第4步—在numCounts的一次传递中,将每个数字正确的次数放入sortedArray
我们这里没有讨论很多其他有趣的排序算法,例如Radix排序,Heap排序,Cube排序等。
我的信念是,我们对基础知识,数据结构或排序算法了解得越多,我们越能更好地理解各种问题并找到解决方案。
破解!
结束。
排序算法时间复杂度