【问题标题】:Search function for a binary tree isn't returning the found node二叉树的搜索功能未返回找到的节点
【发布时间】:2019-11-12 21:56:55
【问题描述】:

这就是我要插入到我的树中的内容:

我正在搜索:Node nd = searchNodeIterativly(root, "Ortiz");

我得到一个空指针错误。

由于"Ortiz" 实际上在树中,我不明白为什么我的循环中的返回不起作用。

是算法,还是我忽略了什么?

这是我的代码:

import java.io.IOException;
import java.util.*;

public class BinaryTree {
    public static class Node {
        String name, number;
        Node Llink, Rlink;
        boolean Ltag, Rtag;

        Node(String name, String number) {
            this.name = name;
            this.number = number;
            Llink = null;
            Rlink = null;
        }
    }

    public static Node insert(Node node, String name, String num) {
        // Searching for a Node with given value
        Node Q = node;
        Node P = null; // Parent of key to be inserted
        while (Q != null) {
            // If key already exists, return
            if (name == (Q.name)) {
                System.out.printf("Duplicate Key !\n");
                return node;
            }
            P = Q;
            if (name.compareTo(Q.name) < 0) {
                if (Q.Ltag == false)
                    Q = Q.Llink;
                else
                    break;
            } else {
                if (Q.Rtag == false)
                    Q = Q.Rlink;
                else
                    break;
            }
        }
        Node tmp = new Node(name, num);
        tmp.name = name;
        tmp.Ltag = true;
        tmp.Rtag = true;

        if (P == null) {
            node = tmp;
            tmp.Llink = null;
            tmp.Rlink = null;
        } else if (name.compareTo(P.name) < 0) {
            tmp.Llink = P.Llink;
            tmp.Rlink = P;
            P.Ltag = false;
            P.Llink = tmp;
        } else {
            tmp.Llink = P;
            tmp.Rlink = P.Rlink;
            P.Rtag = false;
            P.Rlink = tmp;
        }

        return node;
    }

    public static Node searchNodeIterativly(Node node, String name) {
        while (node != null) {
            if (name.compareTo(node.name) > 0) {
                node = node.Llink;
            } else if (name.compareTo(node.name) < 0) {
                node = node.Rlink;
            } else {
                return node;
            }
        }
        return node;
    }

    public static void main(String[] args) throws IOException {
        // BinaryTree tree = new BinaryTree();
        Node root = new Node("Moutafis  ", "295-1492");
        insert(root, "Ikerd     ", "291-1864");
        insert(root, "Gladwin   ", "295-1601");
        insert(root, "Robson    ", "293-6122");
        insert(root, "Dang      ", "295-1882");
        insert(root, "Bird      ", "291-7890");
        insert(root, "Harris    ", "294-8075");
        insert(root, "Ortiz     ", "584-3622");

        Node nd = searchNodeIterativly(root, "Ortiz     ");
        if (nd == null) {
            System.out.println("no result found!");
        } else {
            System.out.println(nd.name + ": " + nd.number);
        }
    }
}

【问题讨论】:

  • 能否展示一下Node类的结构,尤其是方法compareTo和方法insert?
  • “Ortiz”不等于"Ortiz "。 (空格)
  • 我刚改了,我以为就这样了,可惜还是没有。
  • 我认为你走错路了。您必须将根作为属性添加到二叉树类。我做了一个方法来显示你树中的所有节点,它给了我 java.lang.StackOverflowError。

标签: java tree binary-search-tree


【解决方案1】:

长话短说,searchNodeIteratively 的逻辑看起来不错,但 insert 被破坏了,因为它构建了一个无效的树,并且类将从重写中受益。请继续阅读以了解详细信息。


这些类标榜核心 OOP 原则,最明显的违规行为是 re-use。如果 BinaryTree 类中的所有逻辑都是 hard coded 以依赖于 Node 类的 namenum 属性,那么除了存储这两个字段之外,它对于任何目的都是无用的,我们必须重新- 每当我们的数据发生变化时编写整个类。使用generics,我们可以允许BinaryTreeNode 处理任意类型,而无需重新编写任何代码。

类组织是不寻常的——BinaryTree 强制唯一性,所以它实际上是一个BinarySearchTree 类并且应该相应地命名。 insert 应该返回一个布尔值(无论插入是否成功)并且方法不应该是静态的。 main 中的 new BinaryTree 被注释掉了,但这是正确的想法(我们要实例化一个 BST)。将Node 隐藏在BinaryTree 中也很好(这是BST 的实现细节),但它应该是私有的。

其他说明:

  • 使用.equals()而不是==比较对象,如name == (Q.name)== 比较内存位置(这可能是您的意图,但我会质疑这是否是我们认为独特的正确逻辑)。
  • Q, P Rlink 没有过多解释它们是什么并且不遵守 Java 变量命名约定(使用 lowerCamelCase)。除非含义很明显,否则避免使用单字母变量名称。 String num 不清楚--String phoneNumber 更有意义。与其编写 cmets 来解释像 Node P = null; // Parent of key to be inserted 这样不明确的变量名称,不如提供像 Node parent = null; 这样有意义的名称,然后直接解决问题。
  • 返回一个值或抛出一个错误,而不是打印错误消息。打印件是 side effects,无法以编程方式处理。
  • LtagRtag 似乎是冗余信息,使插入逻辑非常混乱。只需检查左右Node对象是否为null即可测试其存在。这样可以减少膨胀并避免布尔值与 Node 值不同步的情况。
  • 插入逻辑:

    tmp.Llink = P.Llink;
    tmp.Rlink = P;
    

    似乎构建了一棵无效的树。这将新节点(tmp)的左链接设置为指向父节点的旧左链接,并将新节点的右链接指向父节点,打破了树的定义,这是一个有根无根的图循环。

  • searchNodeIterativly 应称为 searchNodecontains。从这个类的客户的角度来看,它如何(迭代地)完成这一点应该是无关紧要的(这可能是私有方法的适当名称)。
  • 我们可以看到insert 没有提供树的平衡,因此这基本上将containsinsert 方法的复杂度降低到O(n)。为树添加平衡将是一个很好的下一个添加,将操作带到所需的 O(n log(n))。

这是在BinarySearchTree 类中实现泛型和可重用性的初始重写。我们可以使用 lambda 函数调用 contains 来动态搜索(或者如果这更有意义,我们可以使用 T 的实例)。这些类仅部分实现了类契约——我希望为初学者实现 equalshashCode

import java.util.*;

class BinarySearchTree<T extends Comparable<T>> {
    public interface Comparator<T> {
        int compare(T t);
    }

    private static class Node<T extends Comparable<T>> 
              implements Comparable<Node<T>> {
        private T data;
        private Node left;
        private Node right;

        Node(T data, Node left, Node right) {
            this.data = data;
            this.left = left;
            this.right = right;
        }

        @Override
        public int compareTo(Node<T> node) {
            return this.data.compareTo(node.data);
        }
    }

    private Node<T> root;

    public BinarySearchTree() {
        this.root = null;
    }

    public boolean insert(T item) {
        Node<T> prev = null;

        for (var curr = root; curr != null;) {
            prev = curr;

            if (curr.data.compareTo(item) == 0) {
                return false;
            }

            curr = curr.data.compareTo(item) < 0 ? 
                curr.left : curr.right;
        }

        if (prev == null) {
            root = new Node<T>(item, null, null);
        }
        else if (prev.data.compareTo(item) < 0) {
            prev.left = new Node<T>(item, null, null);
        }
        else {
            prev.right = new Node<T>(item, null, null);
        }

        return true;
    }

    public boolean contains(Comparator<T> cmp) {
        for (var curr = root; curr != null;) {
            if (cmp.compare(curr.data) == 0) {
                return true;
            }

            curr = cmp.compare(curr.data) < 0 ? 
                curr.left : curr.right;
        }

        return false;
    }

    public boolean contains(T item) {
        for (var curr = root; curr != null;) {
            if (curr.data.compareTo(item) == 0) {
                return true;
            }

            curr = curr.data.compareTo(item) < 0 ? 
                curr.left : curr.right;
        }

        return false;
    }

    private static void print(Node node, int depth) {
        if (node != null) {
            print(node.left, depth + 4);

            for (int i = 0; i < depth; i++) {
                System.out.print(" ");
            }

            System.out.println(node.data);
            print(node.right, depth + 4);
        }
    }

    public void print() {
        print(root, 0);
    }
}

class Person implements Comparable<Person> {
    private String name;
    private String phone;

    public Person(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    public String getName() { return name; }
    public String getPhone() { return phone; }
    public String toString() { return name; }

    public int compareTo(Person person) {
        return name.compareTo(person.name);
    }
}

class Main {
    public static void main(String[] args) {
        String[][] data = {
            {"Ikerd", "291-1864"},
            {"Gladwin", "295-1601"},
            {"Robson", "293-6122"},
            {"Dang", "295-1882"},
            {"Bird", "291-7890"},
            {"Harris", "294-8075"},
            {"Ortiz", "584-3622"},
            {"Ortiz", "584-3622"}
        };

        var people = new BinarySearchTree<Person>();

        for (var entry : data) {
            people.insert(new Person(entry[0], entry[1]));
        }

        people.print();
        System.out.println(
            "\nContains \"Bird\"? " + people.contains((p) -> p.getName().compareTo("Bird"))
        );
        System.out.println(
            "Contains \"Birds\"? " + people.contains((p) -> 
                p.getName().compareTo("Birds"))
        );
    }
}

输出:

    Robson
        Ortiz
Ikerd
        Harris
    Gladwin
        Dang
            Bird

Contains "Bird"? true
Contains "Birds"? false

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2015-08-24
    • 1970-01-01
    相关资源
    最近更新 更多