- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个使用列表表示的树。例如:
(1 ((2 (3)) (3 (2)))) (2 ((1 (3)) (3 (1)))) (3 ((1 (2)) (2 (1)))))`
现在我需要在维护层次结构树的同时逐层遍历它。例如:
(1)
(1 2) (1 3) (2 1) (3 1) (3 1) (3 2)
(1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1)
我不知道如何在 Lisp 中做到这一点。感谢任何帮助(甚至是伪代码)。我想到了几种方法,但似乎没有一种是合法的。
最佳答案
进行广度优先搜索的经典方法是维护一个议程:接下来要查看的事情列表。然后,您只需将对象从议程的开头剥离,并将它们的子对象添加到议程的结尾。对于这样的议程,一种非常简单的方法是节点列表:添加到列表的末尾,然后使用 append
。
我无法理解您的树结构(请在询问需要数据结构或算法规范的问题时给出该规范:尝试支持是浪费每个人的时间-猜猜)所以我在列表方面做了自己的:一棵树是一个缺点,它的车是它的值(value),它的 cdr 是 child 的列表。以下是创建和访问此类树结构和示例树的函数。
(defun tree-node-value (n)
(car n))
(defun tree-node-children (n)
(cdr n))
(defun make-tree-node (value &optional (children '()))
(cons value children))
(defparameter *sample-tree*
(make-tree-node
1
(list
(make-tree-node 2 (list (make-tree-node 3)))
(make-tree-node 4 (list (make-tree-node 5) (make-tree-node 6)))
(make-tree-node 7 (list (make-tree-node 8 (list (make-tree-node 9))))))))
现在我再也不用担心树的显式结构了。
现在这是一个使用议程的函数,该议程将在这棵树中搜索给定的节点值:
(defun search-tree/breadth-first (tree predicate)
;; search a tree, breadth first, until predicate matches on a node's
;; value. Return the node that matches.
(labels ((walk (agenda)
(if (null agenda)
;; we're done: nothing matched
(return-from search-tree/breadth-first nil)
(destructuring-bind (this . next) agenda
(if (funcall predicate (tree-node-value this))
;; found it, return the node
(return-from search-tree/breadth-first this)
;; missed, add our children to the agenda and
;; carry on
(walk (append next (tree-node-children this))))))))
(walk (list tree))))
为了比较这里是深度优先搜索:
(defun search-tree/depth-first (tree predicate)
;; search a tree, depth first, until predicate matches on a node's
;; value
(labels ((walk (node)
(if (funcall predicate (tree-node-value node))
(return-from search-tree/depth-first node)
(dolist (child (tree-node-children node) nil)
(walk child)))))
(walk tree)))
您现在可以通过打印其参数但始终失败的谓词来比较这些实现,从而导致遍历整个树:
> (search-tree/breadth-first *sample-tree*
(lambda (v)
(print v)
nil))
1
2
4
7
3
5
6
8
9
nil
> (search-tree/depth-first *sample-tree*
(lambda (v)
(print v)
nil))
1
2
3
4
5
6
7
8
9
nil
这种天真的议程实现的一个问题是我们最终总是调用 append
。更聪明的实现允许将项目有效地附加到末尾。这是这样一个实现:
(defun make-empty-agenda ()
;; an agenda is a cons whose car is the list of items in the agenda
;; and whose cdr is the last cons in that list, or nil is the list
;; is empty. An empty agenda is therefore (nil . nil)
(cons nil nil))
(defun agenda-empty-p (agenda)
;; an agenda is empty if it has no entries in its list.
(null (car agenda)))
(defun agenda-next-item (agenda)
;; Return the next entry from the agenda, removing it
(when (agenda-empty-p agenda)
(error "empty agenda"))
(let ((item (pop (car agenda))))
(when (null (car agenda))
(setf (cdr agenda) nil))
item))
(defun agenda-add-item (agenda item)
;; add an item to the end of the agenda, returning it
(let ((item-holder (list item)))
(if (agenda-empty-p agenda)
(setf (car agenda) item-holder
(cdr agenda) item-holder)
(setf (cdr (cdr agenda)) item-holder
(cdr agenda) item-holder))
item))
请注意,无法复制所提供的这些议程之一。
这是一个明确的迭代函数,它使用了这个“聪明”的议程:
(defun search-tree/breadth-first/iterative (tree predicate)
(loop with agenda = (make-empty-agenda)
initially (agenda-add-item agenda tree)
while (not (agenda-empty-p agenda))
for node = (agenda-next-item agenda)
when (funcall predicate (tree-node-value node))
do (return-from search-tree/breadth-first/iterative node)
else do (loop for c in (tree-node-children node)
do (agenda-add-item agenda c))
finally (return nil)))
最后,任何基于议程的搜索都可以轻松修改为可重新启动:它只需要在匹配点返回当前议程,并允许传递议程。这是支持重新开始搜索的上述功能的变体:
(defun search-tree/breadth-first/iterative (tree predicate
&optional (agenda
(make-empty-agenda)))
;; search TREE using PREDICATE. if AGENDA is given and is not empty
;; instead restart using it (TREE is ignored in this case). Return
;; the node found, or nil, and the remaining agenda
(loop initially (unless (not (agenda-empty-p agenda))
(agenda-add-item agenda tree))
while (not (agenda-empty-p agenda))
for node = (agenda-next-item agenda)
when (funcall predicate (tree-node-value node))
do (return-from search-tree/breadth-first/iterative
(values node agenda))
else do (loop for c in (tree-node-children node)
do (agenda-add-item agenda c))
finally (return (values nil agenda))))
事实上,可以进一步推广基于议程的方法来搜索树。特别是:
对于这两种情况,实际的搜索实现可以是相同的,这很简洁。
下面是一些演示这一点的代码。这定义了树访问的通用函数(使用基于 cons 的树的方法),因此无需关心它,并进一步定义了具有两个具体类的议程协议(protocol),queue
和 stack
有适当的方法。然后,搜索功能完全不知道它是进行深度优先搜索还是广度优先搜索,并且在任何一种情况下都可以重新启动。
这是相当大的一段代码:我把它留在这里以防万一它对任何人都有用。
;;;; Trees
;;;
(defgeneric tree-node-value (n)
(:documentation "The value of a tree node"))
(defgeneric tree-node-children (n)
(:documentation "The children of a tree"))
;;;; Consy trees
;;;
(defmethod tree-node-value ((n cons))
(car n))
(defmethod tree-node-children ((n cons))
(cdr n))
(defun make-cons-tree-node (value &optional (children '()))
;; consy trees: I could do some clever EQL method thing perhaps to
;; abstract this?
(cons value children))
(defun form->tree (form &key (node-maker #'make-cons-tree-node))
(labels ((walk-form (f)
(destructuring-bind (value . child-forms) f
(funcall node-maker
value
(mapcar #'walk-form child-forms)))))
(walk-form form)))
(defparameter *sample-tree*
(form->tree '(1 (2 (3))
(4 (5) (6))
(7 (8 (9))))))
;;;; Agendas
;;;
(defclass agenda ()
())
(defgeneric agenda-empty-p (agenda)
(:documentation "Return true if AGENDA is empty"))
(defgeneric agenda-next-item (agenda)
(:documentation "Return the next item from AGENDA.
If there is no next item, signal an error: there is a before method which does this.")
(:method :before ((agenda agenda))
(when (agenda-empty-p agenda)
(error "empty agenda"))))
(defmethod initialize-instance :after ((agenda agenda) &key
(item nil itemp)
(items (if itemp (list item) '()))
(ordered nil))
(agenda-add-items agenda items :ordered ordered))
(defgeneric agenda-add-item (agenda item)
(:documentation "Add ITEM to AGENDA, returning ITEM.
There is an around method which arranges for ITEM to be returned.")
(:method :around ((agenda agenda) item)
(call-next-method)
item))
(defgeneric agenda-add-items (agenda items &key ordered)
(:documentation "Add ITEMS to AGENDA.
If ORDERED is true do so in a way that AGENDA-NEXT-ITEM will pull them
off in the same order. Return AGENDA (there is an around method which
arranges for this). The default method just adds the items in the
order given.")
(:method :around ((agenda agenda) items &key ordered)
(declare (ignorable ordered))
(call-next-method)
agenda)
(:method ((agenda agenda) items &key ordered)
(declare (ignorable ordered))
(loop for item in items
do (agenda-add-item agenda item))))
;;;; Queues are FIFO agendas
;;;
(defclass queue (agenda)
((q :initform (cons nil nil)))
(:documentation "A queue"))
(defmethod agenda-empty-p ((queue queue))
(null (car (slot-value queue 'q))))
(defmethod agenda-next-item ((queue queue))
(let* ((q (slot-value queue 'q))
(item (pop (car q))))
(when (null (car q))
(setf (cdr q) nil))
item))
(defmethod agenda-add-item ((queue queue) item)
(let ((q (slot-value queue 'q))
(item-holder (list item)))
(if (null (car q))
(setf (car q) item-holder
(cdr q) item-holder)
(setf (cdr (cdr q)) item-holder
(cdr q) item-holder))))
;;;; Stacks are LIFO agendas
;;;
(defclass stack (agenda)
((s :initform '()))
(:documentation "A stack"))
(defmethod agenda-empty-p ((stack stack))
(null (slot-value stack 's)))
(defmethod agenda-next-item ((stack stack))
(pop (slot-value stack 's)))
(defmethod agenda-add-item ((stack stack) item)
(push item (slot-value stack 's)))
(defmethod agenda-add-items ((stack stack) items &key ordered)
(loop for item in (if ordered (reverse items) items)
do (agenda-add-item stack item)))
;;;; Searching with agendas
;;;
(defun tree-search (tree predicate &key (agenda-class 'stack))
;; search TREE using PREDICATE. AGENDA-CLASS (default STACK)
;; defines the type of search: a STACK will result in a depth-first
;; search while a QUEUE will result in a breadth-first search. This
;; is a wrapper around AGENDA-SEARCH.
(agenda-search (make-instance agenda-class :item tree) predicate))
(defun agenda-search (agenda predicate)
;; Search using an agenda. PREDICATE is compared against the value
;; of a tree node. On success return the node matched and the
;; agenda, on failure return NIL and NIL. If the returned agenda is
;; not empty it can be used to restart the search.
(loop while (not (agenda-empty-p agenda))
for node = (agenda-next-item agenda)
when (funcall predicate (tree-node-value node))
do (return-from agenda-search
(values node agenda))
else do (agenda-add-items agenda (tree-node-children node)
:ordered t)
finally (return (values nil nil))))
关于lisp - LISP 广度优先搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54974738/
我正在使用“laravel/lumen-framework”:“5.7.*” 我有两个中间件,第一个 AuthTokenAuthenticate 应该应用于所有路由,因此它在 bootstrap/ap
当同时播放两个音频时...声音会相互抵消。如何解决这个奇怪的现象? 我有一些代码,其中单击按钮时有音频,并且每隔十秒就有音频(在后台服务中)。我有以下代码来在十秒间隔播放时停止按钮音频,并且工作正常:
我有一个功能可以在我的网站上搜索用户, 我的网站上还有一个面向 friend 的功能。 我有一个查询要在我的网站上搜索正确的用户,并且 我有一个查询可以确定用户的 friend ,他们都按应有的方式工
是否可以对记录使用 GROUP BY? 例如,我有一大堆联系人数据,可能包含也可能不包含所有信息 - 在 CSV 意义上,如果可能看起来像这样: Test User, Address1, Addres
如何在客户端 JavaScript 中创建一个环境,其中与用户界面和 View 相关的任何代码优先于其他代码? 我知道你可以使用 setTimeout([function],0); 将事情推到下一个刻
Jasmine 有没有办法定义测试失败的概率? 例如,现在 500'ing 的服务比不显示在页面上的简单内容更糟糕。 谢谢! 最佳答案 这不是单元或集成测试的工作方式。以太测试是否失败。并且您的套件中
我正在为我参与的一个项目开发一个 API。该 API 将由 Android 应用、iOS 应用和桌面网站使用。几乎所有 API 都只有注册用户才能访问。该 API 允许通过 WSSE 进行身份验证,这
我正在开发一些库并创建了这个有缺陷的代码: //------------------- Gmaps = {}; Gmaps.map = new Gmaps4RailsGoogle(); //there
我有一个使用[NSLocale ISOCountryCodes]获得的国家/地区的NSArray。如何排序此NSArray,以便可以将某些常用国家(地区)放在列表的顶部,同时将其余国家/地区按字母顺序
我正在为注册表编写代码,因为我正在从另一个文件中为电话号码列导入代码,但是当我将该代码放入其中时,您可以看到@include('layouts.phone');它显示为 当我放置@include('l
我刚刚遇到了 javascript 代码 file_upload_started = progress < 100; 我不知道如何阅读它,谷歌也没有真正出现太多。我什至不知道该怎么调用它,所以很难进行
目前,我正在 cppinstitute.org 学习 C 语言认证类(class)。在其中一个测验中,有一个如下的问题来识别输出。 int i = 1,j= 1; int w1,w2; w1 = (i
我想将无符号短值从 MSB 优先转换为 LSB 优先。做了下面的代码,但它不工作。有人可以指出我所做的错误吗 #include using namespace std; int main() {
考虑以下场景:我的应用程序有一些依赖于我自己的 POM 优先 Artifact (使用纯 Maven 构建)和一些依赖于我自己的 list 优先 Artifact (使用 Tycho 构建)。对于 P
拥有它应该是很自然的事情,我想知道是否有来自 TPL DataFlow 库的优先级缓冲区块的现成实现? 最佳答案 似乎实现这一目标的最佳方法是使用专门的 任务调度器 ,而不是实现您自己的 Buffer
我有一个 date 字段,它显示为从今天开始的天数。因此 2055-01-01 和 1950-01-01 将分别显示为正数和负数。现在我希望对这些进行排序,以便非负数按升序排在第一位,然后负数按降序排
我遇到一个问题,我看到我的事件类和悬停类正在 Firebug 中应用,但它没有优先于现有样式。 因此,如果我的元素设置了背景颜色,则事件和悬停背景颜色不会更改元素。 我该如何解决这个问题? 最佳答案
我正在考虑为 Salesforce Outbound Messaging 实现监听器应用程序。 walk through 使用已弃用的 ASMX Web 服务实现它。代码是使用带有/serverInt
对于每个表,EF 都会生成一个部分类,其中所有字段都可以公开访问,例如 public int ID { get; set; } 是否可以将 set 设为私有(private)?然后,我将只允许调用我的
我正在为水电站编写一个数据评估应用程序。我需要从服务器下载数据,该数据就在那里 - 作为 MySQL 表,格式化为 JSON 数组。现在,经过无数个小时的工作,我已经完成了连接到服务器、下载数据并将其
我是一名优秀的程序员,十分优秀!