我的任务是创建一个函数,该函数删除由以下项定义的链接队列中所有出现的键:
type 'a qnode = { v: 'a;
mutable next: 'a qnode option }
type 'a queue = { mutable head: 'a qnode option;
mutable tail: 'a qnode option }
我想出了下面列出的代码,该代码通过了我为一个空队列和一个只有一个元素的队列创建的测试,当该队列中有多个元素且该队列中出现一次键时,该超时。
let delete (elt: 'a) (q: 'a queue) : unit =
if not (valid q) then failwith "delete: given invalid queue";
let rec loop (qn: 'a qnode option) (qn2: 'a qnode option) (elt: 'a) : unit =
begin match qn, qn2 with
| None, None -> ()
| None, Some x -> if x.v = elt && x.next = None then
(q.head <- x.next; q.tail <- x.next)
else if x.v = elt && x.next <> None then
(q.head <- x.next; q.tail <- x.next; loop x.next q.tail elt)
else loop x.next q.tail elt
| Some x, Some y -> if y.v = elt && y.next = None then
(x.next <- y.next; q.tail <- y.next)
else if y.v = elt && y.next <> None then
(x.next <- y.next; q.tail <- y.next; loop y.next q.tail elt)
else loop y.next q.tail elt
| Some x, None -> ()
end
in loop None q.head elt
我无法找到确切的地方可能存在无限循环或效率低下的地方,因此将不胜感激。
答案 0 :(得分:1)
循环的基本计划是让qn2
遍历队列的所有节点。在每次调用时,qn
代表队列中qn2
之前的节点。您需要该上一个节点,以便在找到感兴趣的节点时正确更新链接。
当qn2
是队列的第一个节点时,没有前一个节点。因此,在这种情况下,qn
应该是None
。
每次调用都需要考虑两件事:首先,在节点qn2
上要执行的处理。可能会删除它,也可能不会删除它。
第二,如何进行到队列的下一个节点。换句话说,递归调用应该是什么样的?您需要传递一个“上一个”节点(将是新的qn
)和一个当前节点(新的qn2
)。
让我们集中讨论问题的第二部分,即递归调用的细节。假设qn2
不是None
,即有一个要查看的节点。您应该看到,如果删除qn2
,那么当前的qn
还将是下一个呼叫的上一个节点。如果未删除qn2
,则qn2
本身将成为下一个呼叫的上一个节点。无论哪种情况,next
节点的qn2
字段都是要查看的新节点(下一个调用的qn2
)。
如果您查看两个递归调用,则它们都不具有正确的格式。特别是,要查看的下一个节点始终设置为队列的尾部。这是不对的。您不想在处理完第一个节点后立即跳到最后。
此外,如果队列不为空,则q.tail
将始终是节点(而不是None
)。因此循环永远不会终止。