【问题标题】:Search nested lists for an Item在嵌套列表中搜索项目
【发布时间】:2018-02-11 17:19:44
【问题描述】:

假设我有一个类型为 T 的对象,其中包含一个包含 T 类型对象的 ArrayList 字段,我将其称为列表。我还有另一个类型为 T 的对象,我称之为目标。我正在努力寻找目标。为此,我想首先遍历列表以查看目标是否存在。如果是,我想返回原始对象。如果不是,那么我想通过列表逐个对象地检查每个列表的目标(如果找到则返回对象)。我想以递归方式继续此搜索,直到找到匹配项。

我不知道如何做到这一点。我能想到的两个选项是 while 循环和递归。但是,当我检查各种列表时,我必须在各个级别之间摇摆不定,我不知道该怎么做。

我的另一个想法是,我想做的事情与树的水平顺序横向相同。但是,到目前为止,我只了解了二叉树,如果可以在不遍历整个树的情况下进行级别顺序遍历,我不知道如何或是否可以将其转换为树。

下面,看看我到目前为止写的代码。这只会检查第一个列表是否匹配并且不会更深入,这是我需要的。

/**
     * Searches for the shortest path between start and end points in the graph.
     * @param start
     * @param end
     * @return a list of data, starting with start and ending with end, that gives the path through
     * the graph, or null if no such path is found.  
     */
    public List<T> shortestPath(T startLabel, T endLabel){
        List<T> list = new ArrayList<>();
        list.add(startLabel);
        while(true){
            List<T> successors = successorList(startLabel);
            if (containsMatch(successors, endLabel)) {
                findMatch(successors, endLabel);
            }
        }
    }

这种情况有意义吗?如果是这样,有什么想法吗?甚至可能吗? (我尝试搜索,但我的所有查询都没有任何用处)

提前感谢您的帮助。干杯!

【问题讨论】:

  • 你的场景有点道理。您应该将您的数据描述为“图表”,而不是列表列表或其他任何内容,就像您的代码所说的那样。这会更有意义。你有一个图表,你正在寻找的是最短路径。比如说。 (就像下面的第一个答案一样,一开始我还以为你有一棵树。)
  • I tried searching 最短路径问题:en.wikipedia.org/wiki/Shortest_path_problem
  • @markspace 这正是问题所在!我试着把它分解成一个更小的问题,考虑到一个特定的策略,所以我不只是发布我的作业,但也许这不是最好的解决方法——我真的不知道。

标签: java arrays recursion


【解决方案1】:

T 听起来确实像代表一棵树,但这仅适用于其 ArrayList 中的每个 T(以及每个 ArrayList 中的每个 T 等),所有 T 都是唯一的。否则,当它不是时像一棵树一样遍历它可能会导致无限循环。

我不明白您所说的“如果可以在不遍历整个树的情况下进行级别顺序遍历”是什么意思。如果您的树 T 没有秩序感,那么您必须遍历整棵树,因为目标 T 可能在任何地方。这正是你想要做的,不是吗?

将此问题视为两个相互递归的函数可能在概念上有所帮助。一个函数可以称为 SearchT,而另一个函数可以称为 SearchArrayListT。 SearchT 检查 T 是否是“目标”T。如果不是,它在其 T 的 ArrayList 字段上调用 ​​SearchArrayListT。

如果传入的 ArrayList 为空,SearchArrayListT 会产生“假”(即,您表示目标尚未找到。)否则,SearchArrayListT 会在 ArrayList 的每个元素上调用 SearchT,并在每个元素之后检查“是否” true" 被返回(或者你代表找到目标的事实)。这实际上是深度优先搜索,但您应该得到相同的结果。您可以在维基百科页面上查看如何为他们进行广度优先搜索:https://en.wikipedia.org/wiki/Breadth-first_search

具体来说,对于您的问题,您似乎正在寻找从“根”T 到“目标”T 的路径,因此在此相互递归期间,您希望通过“到目前为止的路径”,并且沿着递归附加到“到目前为止的路径”。更具体地说,SearchT 将在 pathSoFar 中附加 take,然后调用 SearchArrayListT,pathSoFar 附加 SearchT 也作为参数接收的“T”。像这样的:

SearchT(T t, List<T> pathSoFar) //append t to pathSoFar, check if 
//t is the goal; if it is not call SearchArrayListT(t.list, pathSoFar.add(t)); 

SearchArrayListT(ArrayList<T>, List<T> pathSoFar)

【讨论】:

  • 非常感谢您的建议!我的深度优先问题是我们必须检查每条路径以确定最短的路径。广度不是更有效率吗?此外,如果两个项目在彼此的列表中,我们可能会遇到无限循环。我一定会查看 wiki 链接。谢谢!!!
  • 你说得对,它会更有效,我只是发现深度搜索方法更容易递归实现(因此更容易解释)。至于遇到无限循环的可能性,那么我们肯定不会使用树,您需要做的是跟踪图中的节点何时被访问。在四处搜索时,我已经找到了 Java 代码的答案:stackoverflow.com/a/37066075/8684344
【解决方案2】:

如果您尝试递归查找图表中的项目,您可以使用depth first search

在遍历过程中,您需要标记访问过的节点,这样您就不会一直循环访问它们。您只会访问节点的子节点,以防它们尚未被访问。

这是一个使用 DFS 的简单 Java 实现,它递归地在树中搜索树中的值,然后如果找到则捕获节点。

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TreeSearch<T> {

    private final Set<T> marked = new HashSet<>();

    private boolean found;

    private Node<T> foundNode;

    public TreeSearch(Node<T> node, T s) {
        dfs(node, s);
    }

    private void dfs(Node<T> node, T s) {
        if(node.value.equals(s)) {
            found = true;
            foundNode = node;
        }
        marked.add(node.value);
        if(node.children != null) {
            for (Node<T> child : node.children) {
                if (!marked.contains(child.value)) {
                    dfs(child, s);
                }
            }
        }
    }

    public boolean isFound() {
        return found;
    }

    public Node<T> getFoundNode() {
        return foundNode;
    }

    public static void main(String[] args) {
        Node<Integer> root = new Node<>(0);
        Node<Integer> n1 = new Node<>(1);
        Node<Integer> n2 = new Node<>(2);
        root.add(n1);
        root.add(n2);
        n2.add(n1);
        Node<Integer> n3 = new Node<>(3);
        n2.add(n3);

        TreeSearch<Integer> search = new TreeSearch<>(root, 3);
        assert search.isFound();
        System.out.println(search.isFound()); // Expects true
        System.out.println(search.getFoundNode());

        TreeSearch<Integer> searchFail = new TreeSearch<>(root, 4);
        assert !searchFail.isFound();
        System.out.println(searchFail.isFound()); // Expects false
    }

}

class Node<T> {

    T value;

    List<Node<T>> children;

    public Node(T value) {
        this.value = value;
    }

    public void add(Node<T> child) {
        if(children == null) {
            children = new ArrayList<>();
        }
        children.add(child);
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                ", children=" + children +
                '}';
    }
}

如果你运行这个类(它有一个只用于测试目的的 main 方法)你会看到这个输出:

true
Node{value=3, children=null}
false

这个算法不会告诉你最短路径。它只会告诉您一个值是否在图中以及在哪个节点中。

如果您试图从有向图中的源节点找到最短路径,最好使用Breadth-first search

【讨论】:

    猜你喜欢
    • 2013-11-26
    • 2011-10-16
    • 2023-04-10
    • 2018-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多