从序列中获取n个最小数字的最有效方法是什么
[ [1 2 3] [9 2 1] [2 3 4] [5 6 7] ]
我想根据第一项
从序列中取2个最小值[1 2 3] [2 3 4]
目前我正在对整个列表进行排序,然后采取前n项,但这可能不是最有效的方法,这是一个很大的列表,我需要经常这样做。
答案 0 :(得分:3)
如上所述,您可以使用中位数中值算法在线性时间中选择第k个最小元素,然后在线性时间内进行分区。这将为您提供O(n)中的k个最小元素。然而,元素将是未排序的,因此如果您希望排序的k个最小元素将花费您另一个O(klogk)。
一些重要的注意事项:
首先,虽然复杂度为O(n),但小的常数并不能得到保证,并且您可能会发现最小的改进,特别是如果您的n相当小。有一些随机线性选择算法可以在更好的实际时间内运行(通常预期的运行时间是O(n),最坏的情况更糟,但它们的常数比确定的更小)。
为什么你不能以排序的方式维护数组?这可能会更高效。您只需要将每个元素插入到正确的位置,这需要花费O(logn),但是找到k最小值则为O(1)(如果必须重新构建数组,则为O(k))。
如果你决定反对上面的注释,那么另一种方法是在每个这样的过程之后保持数组排序,在数组的末尾提供插入O(1),然后每次执行“合并排序”你需要找到k个最小元素的时间。即您只需 新的,然后在线性时间内合并它们。因此,这将花费O(mlogm + n),其中m是自上次排序以来添加的元素数量。
答案 1 :(得分:3)
Joy of Clojure,第6.4章描述了一种惰性排序算法。懒惰排序的优点在于它只需要做很多工作就可以找到前面的x值。所以如果x<<这个算法是O(n)。这是该算法的修改版本。
(defn sort-parts
[work f]
(lazy-seq
(loop [[part & parts] work]
(if-let [[pivot & xs] (seq part)]
(let [psmaller? (partial f pivot)]
(recur (list* (filter psmaller? xs)
pivot
(remove psmaller? xs)
parts)))
(when-let [[x & parts] parts]
(cons x
(sort-parts parts f)))))))
(defn qsort [xs f] (sort-parts (list xs) f))
(defn cmp [[a _ _] [b _ _]] (> a b))
(def a [[1 2 3] [9 2 1] [2 3 4] [5 6 7]])
(take 2 (qsort a cmp))
答案 2 :(得分:0)
如果n很小,您可以创建第二个大小为n的列表,并保持排序,这样您就可以快速访问该列表中的最大值; 遍历大列表,检查每个小于列表中的最大值; 如果是这样,将其插入小清单......小清单已满,弹出之前的最旧清单。
如果n小于3或4,你可以强制它。如果n可以更大,您将需要进行二分搜索以找到每个的插入点。如果n可能非常大,那么可能会有不同的机制。