F#有效地从集合的末尾删除n个项目

时间:2010-08-04 20:02:42

标签: data-structures f# immutability

我知道我可以删除集合中的最后一个元素:

s.Remove(s.MaximumElement)

但是如果我想删除n个最大元素......我只执行上述n次,还是有更快的方法呢?

要明确,这是一个明显的解决方案:

let rec removeLastN (s : Set<'a>, num : int) : Set<'a> = 
    match num with
    | 0 -> s
    | _ -> removeLast(s.Remove(s.MinimumElement), num-1)

但它涉及创建一个新的n次。有没有办法做到这一点,只创建一个新集合?

3 个答案:

答案 0 :(得分:1)

  

但它涉及创建一个新的集合n   倍。有没有办法做到这一点   只创建一个新集合?

据我所知,没有。我会说你有一个非常好的实现,它运行在O(lg n) - 它也简洁:)大多数堆实现给你O(lg n)for delete min,所以你拥有的是关于as好,你可以得到它。

您可以通过滚动平衡树来获得更快的速度,并实现一个函数来删除大于某个值的所有值的左或右分支。我不认为AVL树或RB树在这种情况下是合适的,因为你不能真正维护它们的不变量,但是随机化的树将为你提供你想要的结果。

treap对此非常有用,因为它使用随机化而不是树不变量来保持自身相对平衡。与AVL树或RB树不同,您可以在节点上拆分treap而不必担心它是不平衡的。这是我几个月前写的一个treap实现:

http://pastebin.com/j0aV3DJQ

我添加了一个split函数,它允许你获取一棵树并返回两个包含所有小于的值和所有大于给定值的值的树。 split使用一次遍历树在O(lg n)中运行,因此您可以一次性修剪树的整个分支 - 前提是您知道要拆分的值。

  

但是如果我想删除n的最大值   元素...我只是执行   超过n次,还是更快   这样做的方法?

使用我的Treap课程:

open Treap

let nthLargest n t = Seq.nth n (Treap.toSeqBack t)
let removeTopN n t =
    let largest = nthLargest n t
    let smallerValues, wasFound, largerValues = t.Split(largest)
    smallerValues

let e = Treap.empty(fun (x : int) (y : int) -> x.CompareTo(y))
let t = [1 .. 100] |> Seq.fold (fun (acc : Treap<_>) x -> acc.Insert(x)) e
let t' = removeTopN 10 t

removeTopN在O(n + lg m)时间运行,其中n是树序列的索引,m是树中项目的数量。

我不保证我的代码的准确性,使用你自己的危险;)

答案 1 :(得分:0)

这已经是一个非常好的解决方案。 OCaml有一个split函数可以拆分Set,因此您可以找到正确的元素,然后您可以拆分Set一次删除一堆元素。或者,您可以使用Set.difference提取另一个Set元素。

答案 2 :(得分:0)

在F#中,您可以使用Set.partitionSet.filter创建子集:

let s = Set([1;4;6;9;100;77])

let a, b = Set.partition (fun x -> x <= 10) s

let smallThan10 = Set.filter (fun x -> x < 10) s

在你的问题中,也许你不知道你的集合的第i个数字的值,所以这里有一个方便的功能:

let nth (n:int) (s:'a Set) = 
    s |> Set.toSeq |> Seq.nth n

现在,我们可以编写remove-top-n函数:

let removeTopN n (s:'a Set) = 
    let size = s.Count
    let m = size - n
    let mvalue = nth m s
    Set.filter (fun x -> x < mvalue) s

并测试它:

removeTopN 3 s

我们得到:

val it : Set<int> = set [1; 4; 6]

请注意,removeTopN不适用于包含多个相同值的集合。