我想解决的问题是在非有向图中找到最少数量的子树,其中每个节点都在一个子树中。
我的算法如下:
make a hash as follows
key= each node,
value= all nodes directly accessible from the key node
if a node has no edges it still has a key/value pair in the hash
each edge will be represented twice, once each direction
loop until hash is empty
get any hash key/value pair
save the value to the working-list
remove the key/value from the hash
in a loop until the working-list is empty
(when (gethash (car working-list) hash)
concatenate value to the end of the working-list
(remhash (car working-list) hash))
(pop working-list)
When the loop is finished you have removed all nodes and edges in a single subtree from the hash.
Increment number of subtrees.
end loop until the hash is empty.
report number of subtrees
以下是代码:
(defun count-subtrees (hash)
; hash
; key= each node,
; value= all nodes directly accessible from the key node
; if a node has no edges it still has a key/value pair in the hash
; each edge will be represented twice, once each direction
(let ((number-of-trees 0))
(while (setf key (anykey-in-hash hash)) ; this is where I need any key in the hash
(setf working-list (gethash key hash))
(remhash key hash)
(while (gethash (car working-list) hash)
(setf working-list (append working-list
(gethash (car working-list hash))))
(remhash (car working-list) hash)
(pop working-list))
(incf number-of-trees))
number-of-trees))
我不想迭代键,我想只得到一个。
注意:
谢谢大家的回复。我正在指导这些意见。
编辑改变了我的问题,添加了“随机”一词。我不在乎它是否是随机的。回复:
(defun anykey-in-hash (hash-table)
(block nil
(maphash (lambda (k v) (return (values k v)))
是一个完美的解决方案。
我无意中使用了'while'(我从Paul Graham的书中得到的)而没有定义它。我希望它不会让任何人感到困惑。
我也使用setf代替let来定义变量。愚蠢的错误,我永远不会那样做。
最初我有一个所有键的列表。但在算法期间,键/值对被删除。这就是为什么我需要让任何人离开。
我也使用工作列表作为列表。 working-list包含子树中的一些节点。需要从散列中删除所有这些节点(及其子节点*和它们的子节点......)。附加到此列表是为了简化代码。在实际的应用程序中,我会使用其他一些数据结构,可能是列表列表。但我觉得这样做会增加复杂性而不会在问题的含义上添加任何内容。
*当我说孩子时,我的意思是使用节点作为哈希的关键,值是孩子的列表。
答案 0 :(得分:3)
您正在设置全局变量而不事先声明它们,这是不可移植的,并且由于副作用通常不安全;更喜欢让绑定。
至于获取“随机”键,您可以像这样简单地实现anykey-in-hash
:
(defun anykey-in-hash (hash-table)
(block nil
(maphash (lambda (k v) (return (values k v)))
hash-table)))
存储在散列表中的元素的顺序取决于实现,因此maphash
将以任意(但可能是确定的)顺序迭代条目。如果你真的需要一个随机顺序,即一个可以用相同参数的函数调用进行更改的顺序,你必须收集密钥并随机选择一个。
在您的示例中,您似乎只需要计算一次键列表,而不是在while循环的每次迭代中。我个人会将所有密钥放在一个列表中, shuffle 一次,然后根据需要 pop 元素。
我最初的代码是在Common Lisp中。在CL中,Alexandria library定义shuffle
(和random-elt
),以及hash-table-keys
(或hash-table-alist
,如果您同时需要密钥和值)。
在Emacs Lisp中,您也可以轻松地使用hash-table-keys
实现maphash
(您可以从maphash调用的函数内部推送列表前面的键)。如果您有Emacs 24.4,则可(require 'subr-x)
并致电get-hash-keys
。可以像在CL中那样进行改组,你可以从alexandria(https://gitlab.common-lisp.net/alexandria/alexandria/blob/master/sequences.lisp#L90)调整算法,知道你只关心参数是列表的情况。