功能O(1)从第一个元素列表数据结构追加和O(n)迭代

时间:2011-03-16 11:31:22

标签: data-structures f# functional-programming

我正在寻找支持以下操作的功能数据结构:

  • 追加,O(1)
  • 按顺序迭代,O(n)

正常的功能链接列表仅支持O(n)追加,而我可以使用普通LL然后反转它,反向操作也是O(n),它(部分地)否定O(1)缺点操作

5 个答案:

答案 0 :(得分:9)

你可以使用John Hughes的常数追加列表,这些列表现在似乎被称为DList。表示是从列表到列表的函数:空列表是标识函数;追加是组合,单身是缺点(部分应用)。在此表示中,每个枚举将花费您n分配,因此可能不太好。

另一种方法是将相同的代数作为数据结构:

type 'a seq = Empty | Single of 'a | Append of 'a seq * 'a seq

枚举是一个树遍历,它要么花费一些堆栈空间,要么需要某种拉链表示。这是一个转换为list但使用堆栈空间的树遍历:

let to_list t =
  let rec walk t xs = match t with
  | Empty -> xs
  | Single x -> x :: xs
  | Append (t1, t2) -> walk t1 (walk t2 xs) in
  walk t []

这是相同的,但使用不变的堆栈空间:

let to_list' t =
  let rec walk lefts t xs = match t with
  | Empty -> finish lefts xs
  | Single x -> finish lefts (x :: xs)
  | Append (t1, t2) -> walk (t1 :: lefts) t2 xs
  and finish lefts xs = match lefts with
  | [] -> xs
  | t::ts -> walk ts t xs in
  walk [] t []

您可以编写一个折叠函数来访问相同的元素,但实际上并没有重新列出该列表;只需用更通用的东西替换cons和nil:

val fold : ('a * 'b -> 'b) -> 'b -> 'a seq -> 'b

let fold f z t =
  let rec walk lefts t xs = match t with
  | Empty -> finish lefts xs
  | Single x -> finish lefts (f (x, xs))
  | Append (t1, t2) -> walk (t1 :: lefts) t2 xs
  and finish lefts xs = match lefts with
  | [] -> xs
  | t::ts -> walk ts t xs in
  walk [] t z

这是你的线性时间,常量堆栈枚举。玩得开心!

答案 1 :(得分:5)

我相信你可以使用标准功能链表:

  • 要附加元素,您可以使用缺点 O(1)
  • 要按插入顺序迭代元素,您可以先反转列表,
    (它是 O(N))然后遍历它,它也是 O(N)(而 2xO(N)仍然只是 O(N))。

答案 2 :(得分:5)

差异清单怎么样?

type 'a DList = DList of ('a list -> 'a list)

module DList =
  let append (DList f) (DList g) = (DList (f << g))
  let cons x (DList f) = (DList (fun l -> x::(f l)))
  let snoc (DList f) x = (DList (fun l -> f(x::l)))
  let empty = DList id
  let ofList = List.fold snoc empty
  let toList (DList f) = f []

答案 3 :(得分:1)

circularly-linked list怎么样?它支持O( 1 )追加和O( n )迭代。

答案 4 :(得分:1)

您可以创建一个功能性Deque,它可以向任意一端添加O(1),并在任一方向上提供O(N)迭代。 Eric Lippert写了一篇关于不可变的Deque on his blog的有趣版本的文章,如果你环顾四周,你会发现该系列的其他部分,但这是对最终产品的解释。另请注意,通过稍微调整,可以修改它以利用F#区分联合和模式匹配(尽管这取决于您)。

此版本的另一个有趣属性,O(1)从任一端偷看,删除和添加(即dequeueLeft,dequeueRight,dequeueLeft,dequeueRight等仍然是O(N),而O(N * N) )使用双列表方法)。