我是学习算法的新手 - 我也不是计算机科学专业的毕业生
但是,在阅读线性排序非比较算法时,我可以理解基数排序是计数排序的扩展。
我不清楚的是计数排序的限制
为什么在计算排序时我会选择基数排序似乎是为了避免进行O(n * logn)比较而需要的目的?
它确实似乎是一个更简单的实现。
答案 0 :(得分:3)
想象一下有人给你一个整数列表来排序。你对它一无所知,只不过它包含整数。
如果你很幸运,列表可能包含一个相当紧密的数字。如果你正在排序整数介于-100和100之间的整数,那么为了进行计数排序而创建一个具有该大小的数组就不会有坏处。
但即使一个数字非常大或非常小,您现在必须扩展数组的边界,以便对整个输入进行计数排序。如果你真的想要对所有可能的整数进行排序(并且在创建数组之前你不知道值的范围,除非你先找到它),你需要创建一个大小为2 * max_int
的数组(对于负整数和正整数)。
基数排序很好,因为你永远不需要创建一个大小超过数字范围(0-9)的数组。
答案 1 :(得分:2)
计数排序算法(包括Radix)仅适用于可数元素。不幸的是,实数不可数,因此您无法轻松排序“浮动”或“双重”值。想象一下,您需要对测量温度列表进行排序。
现在关于可数量(如整数),假设从数组中获取元素是O(1),则会有一个基本错误。这不是真的。当你有大小为N的数组时,将指针设置到这个数组的成本是O(log(N))。换句话说,要访问元素Array [i],您需要定义'i'并定义'i'的值,您需要设置log(i)位。 只要N很小(比如使用计数排序在-100..100之间排序值为200),我们假设log(N)是常数并且忽略它。但是如果你想整数排序比计数数组大(大小:2 * MAX_INT),log(2 * MAX_INT)可能是一个很大的数字(如32)。 所以假设你有一个100的数组:一个整数的[100]。 使用O(N * log(N))排序需要O(100 * log(100))比较。 但是当使用计数排序时你创建一个巨大的计数数组(对于64位整数整数说2 ^ 64)你的总时间是O(N * log(2 ^ 64)),实际上大于O(100 * log( 100))。听起来很疯狂这是真的。 并且考虑一下在开始计数之前需要将整个计数数组设置为零的事实 - 这是2 ^ 64次操作,远远超过整个O(100 * log(100))... 还要考虑一下巨大的内存浪费......
总结:即使你有无限的内存使用,运行时间也不是真的O(N)。实际上是将计数数组归零并执行计数的成本:
O(MAX_INT) + O(N*log(MAX_INT))
通常,对于任何合理的N,这远远超过O(N*log(N))
,因此计算排序是不切实际的。唯一可行的情况是当值的范围很小时
(如-100..100)和
O(MAX_INT) + O(N*log(MAX_INT))
变为O(200) + O(N*log(200)) ~ O(N)
基数排序使您可以节省一些内存和将大量计数数组归零的成本,但是您仍然没有真正松开log()因子,因为多个范围-X..X具有log(X)数字并且您仍然具有通常大于log(N)的日志(MAX_INT),其中N是您要排序的数组的大小。
答案 2 :(得分:1)
计数排序具有复杂度O(max - min),其中min,max是您要排序的最小和最大整数。如果此范围远大于您要排序的数组的大小,则基数排序更好。
答案 3 :(得分:1)
我不赞同其中一些答案。 First Radix Sort可以对双打和浮动进行排序。我已经完成了它,它仍然比比较分类快得多。
对于操作,您可以通过查看我之前撰写的这篇文章了解更多信息。它总是最好的线性时间排序。
答案 4 :(得分:0)
当人们谈论算法时,他们通常会表达算法在时间和内存要求方面的表现
正如你所看到的,计数排序很棒。它以线性时间运行
但它也需要O(N)
内存要求
当我们寻找算法时,我们经常会在内存和时间复杂度之间进行权衡。
通过使用更多内存,我们可以获得更好的运行时间
因此,虽然计数排序具有更好的时间复杂度,但它需要与输入大小成比例的空间,这使得在大多数情况下使用它是不切实际的。
作为一个更严重的问题,你需要事先知道输入中的数字范围。确保编码它简单而优雅,但是当涉及到实际应用时,它是有限的。