在Common Lisp中表示有向无环图

Xia*_*ian 2 lisp graph-theory graph common-lisp directed-acyclic-graphs

通常,为了代表Lisp中的基本无向图,我可以创建父节点及其对应的子节点的列表,如本问题所述(为方便起见,在下面进行了说明)。

在此处输入图片说明

该图产生边的列表:

(1 (2 6 7 8) 3 (4 (9 12)) 5 (10 11)) 
Run Code Online (Sandbox Code Playgroud)

这在树或任何其他无向图的情况下有效。但是,当我们要表示一个有向无环图(其中每个节点可以有多个父级)时,这种表示形式就会失效:

在此处输入图片说明

现在,节点8具有多个父节点(2、3),但是当我们尝试表示它时,我们将无法判断节点8是否连接到两个父节点,或者是否存在两个节点8:

(1 (2 6 7 8) (3 8) (4 (9 12)) (5 10 11))
Run Code Online (Sandbox Code Playgroud)

对于具有唯一节点的图,我们当然可以做出这种假设,但是据我所知,DAG可以具有重复的节点...那么我们如何处理呢?此外,我们如何在Lisp中将其表示为列表?

sds*_*sds 5

表示DAG的正确方法是节点(顶点)的集合:

(defstruct node
  payload
  children)
Run Code Online (Sandbox Code Playgroud)

由于该结构只有两个插槽,因此当然可以使用一个 cons单元。

您提供的树的列表表示形式将有效负载与无子节点合并, 并弄乱了最右边的分支。正确的表示是

(1 (2 (6) (7) (8)) (3) (4 (9 (12))) (5 (10) (11)))
Run Code Online (Sandbox Code Playgroud)

现在,DAG是股无子女的节点(8)节点的子女之间(2 ...)(3 ...)应该仅仅共享单元:

(1 (2 (6) (7) #1=(8)) (3 #1#) (4 (9 (12))) (5 (10) (11)))
Run Code Online (Sandbox Code Playgroud)

请参阅#n=#n#用于读者符号。当然,您不应使用它们来创建 DAG。

这是创建DAG的方法:

(defun make-node (&key payload children)
  (cons payload children))
(defparameter *my-dag*
  (let ((shared-mode (make-node :payload 8)))
    (make-node
     :payload 1
     :children
     (list (make-node :payload 2
                      :children (list (make-node :payload 6)
                                      (make-node :payload 7)
                                      shared-mode))
           (make-node :payload 3
                      :children (list shared-mode))
           (make-node :payload 4
                      :children (list (make-node :payload 9
                                                 :children (list (make-node :payload 12)))))
           (make-node :payload 5
                      :children (list (make-node :payload 10)
                                      (make-node :payload 11)))))))
(setq *print-circle* t)
*my-dag*
==> (1 (2 (6) (7) #1=(8)) (3 #1#) (4 (9 (12))) (5 (10) (11)))
Run Code Online (Sandbox Code Playgroud)