zoukankan      html  css  js  c++  java
  • 实现二叉搜索树

    树形结构中最重要的就是二叉树,很多经典的算法与数据结构其实都是通过二叉树演变而来。

    二叉树:一种特殊的树形结构,每个节点至多只有两颗子树

    满二叉树:除叶子结点外每个结点都有左右两个子结点

    完全二叉树:除最后一层之外的结点个数达到最大,并且最后一层结点都连续靠左排列

    二叉搜索树:

      1.如果它的左子树不为空,则左子树上结点的值都小于根结点。

      2.如果它的右子树不为空,则右子树上结点的值都大于根结点。

      3.子树同样也要遵循以上两点

    使用链表实现一个二叉搜索树,测试其遍历、查找、插入和删除方法

    public class Node {
        private int  data;
        private Node left;
        private Node right;
        private Node parent;
    
        public Node(int data, Node left, Node right) {
            this.data = data;
            this.left = left;
            this.right = right;
        }
    
        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    '}';
        }
    }
    public class BinarySearchTree {
    
        /**
         * 从某个节点开始找到data所在的节点
         *
         * @return
         */
        public static Node find(Node root, int data) {
            Node cur = root;
            while (null != cur) {
                if (data < cur.getData()) {
                    cur = cur.getLeft();
                } else if (data > cur.getData()) {
                    cur = cur.getRight();
                } else {
                    return cur;
                }
            }
            return null;
        }
    
        /**
         * 输出节点
         *
         * @param node
         */
        public static void print(Node node) {
            if (null != node) {
                System.out.print(node.getData() + ",");
            }
        }
    
        /**
         * 前序遍历:根左右,10,6,3,2,1,4,5,8,7,9,15,13,12,11,14,17,16,18,19,20
         *
         * @param root
         */
        public static void pre(Node root) {
            print(root);
            if (null != root.getLeft()) {
                pre(root.getLeft());
            }
            if (null != root.getRight()) {
                pre(root.getRight());
            }
        }
    
        /**
         * 中序遍历:左根右,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,18,20
         *
         * @param root
         */
        public static void mid(Node root) {
            if (null != root.getLeft()) {
                mid(root.getLeft());
            }
            print(root);
            if (null != root.getRight()) {
                mid(root.getRight());
            }
        }
    
        /**
         * 后序遍历:左右根,1,2,5,4,3,7,9,8,6,11,12,14,13,16,19,20,18,17,15,10
         *
         * @param root
         */
        public static void post(Node root) {
            if (null != root.getLeft()) {
                post(root.getLeft());
            }
            if (null != root.getRight()) {
                post(root.getRight());
            }
            print(root);
        }
    
        /**
         * 层次遍历:一层一层的取出数据,借用队列先进先出的特性实现
         * 10,6,15,3,8,13,17,2,4,7,9,12,14,16,18,1,5,11,19,20
         *
         * @param root
         */
        public static void levelOrder(Node root) {
            Queue<Node> queue = new ArrayDeque<>();
            queue.offer(root);
            while (!queue.isEmpty()) {
                Node cur = queue.poll();
                System.out.print(cur.getData() + ",");
                if (null != cur.getLeft()) {
                    queue.offer(cur.getLeft());
                }
                if (null != cur.getRight()) {
                    queue.offer(cur.getRight());
                }
            }
        }
    
        /**
         * 计算某个节点的高度
         *
         * @param root
         * @return
         */
        public static int getTreeHight(Node root) {
            return root == null ? 0 : 1 + Math.max(getTreeHight(root.getLeft()), getTreeHight(root.getRight()));
        }
    
        /**
         * 插入的过程其实就是查找的过程。插入的位置是叶子节点
         *
         * @param data
         */
        public static void insert(Node root, int data) {
            if (root.getData() < data) {
                if (root.getRight() != null) {
                    insert(root.getRight(), data);
                } else {
                    Node newNode = new Node(data, null, null);
                    newNode.setParent(root);
                    root.setRight(newNode);
    
                }
            } else {
                if (root.getLeft() != null) {
                    insert(root.getLeft(), data);
                } else {
                    Node newNode = new Node(data, null, null);
                    newNode.setParent(root);
                    root.setLeft(newNode);
                }
            }
        }
    
        /**
         * 找后继结点,右边第一个左节点为空的节点
         *
         * @param node
         * @return
         */
        public static Node findSuccessor(Node node) {
            if (node.getRight() == null) {
                return node;
            }
            Node cur = node.getRight();
            Node pre = node.getRight();
            while (cur != null) {
                pre = cur;
                cur = cur.getLeft();
            }
            return pre;
        }
    
        /**
         * 删除,分几种情况,左为空,右为空,全为空,全不为空
         *
         * @param root
         * @param data
         */
        public static Node remove(Node root, int data) {
            // 找到要删除的节点
            Node delNode = find(root, data);
            if (delNode == null) {
                System.out.println(data + "节点为空");
                return root;
            }
            // 左节点和右节点都为空,这是最简单的情况
            if (delNode.getLeft() == null && delNode.getRight() == null) {
                // 如果删除的是根节点,将根节点置空即可
                if (delNode == root) {
                    root = null;
                    return root;
                }
                // 非根节点,有父节点,判断要删除的节点是父节点的左节点还是右节点,对应置空
                else if (delNode.getParent().getData() < delNode.getData()) {
                    delNode.getParent().setRight(null);
                } else {
                    delNode.getParent().setLeft(null);
                }
            }
            // 最复杂的情况,要删除的节点的左右节点都不为空
            else if (delNode.getLeft() != null && delNode.getRight() != null) {
                // 找到要删除节点的后继结点
                Node successor = findSuccessor(delNode);
                // 将后继结点移动到删除节点的位置
                successor.setLeft(delNode.getLeft());
                // 后继结点的左节点的父节点赋值为后继结点
                successor.getLeft().setParent(successor);
                // 后继结点的右节点不为空,并且后继结点的父节点不是要删除的节点
                if (successor.getRight() != null && successor.getParent() != delNode) {
                    // 后继结点的右节点的父节点指向后继结点的父节点,就相当于把后继结点移走了
                    successor.getRight().setParent(successor.getParent());
                    // 后继结点的父节点的左节点,指向后继结点的右节点
                    successor.getParent().setLeft(successor.getRight());
                    // 后继结点的右节点指向原删除节点的右节点
                    successor.setRight(delNode.getRight());
                    // 原删除节点的右节点现在变成后继结点的右节点,所以要改变其父节点
                    successor.getRight().setParent(successor);
                }
                // 后继结点的右节点为空
                else if (successor.getRight() == null) {
                    // 如果后节点的父节点不为空,需要指针重新赋值
                    if (successor.getParent() != delNode) {
                        successor.getParent().setLeft(null);
                        // 剩下的和上哪面一样了,后继上移至删除节点位置,指针重新指向对应的子节点和父节点
                        successor.setRight(delNode.getRight());
                        successor.getRight().setParent(successor);
                    }
                }
                // 要删除的节点是根节点,置空其父节点
                if (delNode == root) {
                    successor.setParent(null);
                    root = successor;
                    return root;
                }
                // 后继结点的父节点指向原删除节点的父节点
                successor.setParent(delNode.getParent());
                // 原删除节点的子节点指向后继结点
                if (delNode.getData() > delNode.getParent().getData()) {
                    delNode.getParent().setRight(successor);
                } else {
                    delNode.getParent().setLeft(successor);
                }
            }
            // 删除节点的左节点或者右节点其中之一为空
            else {
                // 右节点不为空
                if (delNode.getRight() != null) {
                    // 如果删除的是根节点,将右节点上移到根节点即可,而且左节点为空,不需要处理
                    if (delNode == root) {
                        root = delNode.getRight();
                        return root;
                    } else {
                        // 非根节点情况
                        delNode.getRight().setParent(delNode.getParent());
                        if (delNode.getData() < delNode.getParent().getData()) {
                            delNode.getParent().setLeft(delNode.getRight());
                        } else {
                            delNode.getParent().setRight(delNode.getRight());
                        }
                    }
                } else {
                    if (delNode == root) {
                        root = delNode.getLeft();
                        return root;
                    } else {
                        delNode.getLeft().setParent(delNode.getParent());
                        if (delNode.getData() < delNode.getParent().getData()) {
                            delNode.getParent().setLeft(delNode.getLeft());
                        } else {
                            delNode.getParent().setRight(delNode.getLeft());
                        }
                    }
                }
            }
            return root;
        }
    
        public static void main(String[] args) {
    // 构建一个二叉搜索树,测试上面的方法 Node node1
    = new Node(1, null, null); Node node5 = new Node(5, null, null); Node node7 = new Node(7, null, null); Node node9 = new Node(9, null, null); Node node11 = new Node(11, null, null); Node node14 = new Node(14, null, null); Node node16 = new Node(16, null, null); Node node20 = new Node(20, null, null); Node node18 = new Node(18, null, null); Node node2 = new Node(2, node1, null); Node node4 = new Node(4, null, node5); Node node3 = new Node(3, node2, node4); Node node8 = new Node(8, node7, node9); Node node12 = new Node(12, node11, null); Node node13 = new Node(13, node12, node14); Node node19 = new Node(19, node18, node20); Node node17 = new Node(17, node16, node19); Node node6 = new Node(6, node3, node8); Node node15 = new Node(15, node13, node17); Node root = new Node(10, node6, node15); node1.setParent(node2); node5.setParent(node4); node2.setParent(node3); node4.setParent(node3); node7.setParent(node8); node9.setParent(node8); node11.setParent(node12); node12.setParent(node13); node14.setParent(node13); node18.setParent(node19); node20.setParent(node19); node16.setParent(node17); node19.setParent(node17); node3.setParent(node6); node8.setParent(node6); node13.setParent(node15); node17.setParent(node15); node6.setParent(root); node15.setParent(root); // mid(root); // Node node = find(root, 2); // System.out.println(node.toString()); // pre(root); // System.out.println(); // mid(root); // System.out.println(); // post(root); // System.out.println(); // levelOrder(root);
    root= remove(root, 15); mid(root); // Node successor = findSuccessor(node4); // System.out.println(successor); } }

    输出结果就不展示了......

  • 相关阅读:
    大白痴学习webmagic
    selenium docs
    centos6.5单用户模式拯救系统
    使用Squid做代理服务器,Squid单网卡透明代理配置详解(转)
    Redundant Call to Object.ToString()
    Specify a culture in string conversion explicitly
    Benefits of Cold Showers: 7 Reasons Why Taking Cool Showers Is Good For Your Health
    Why Creating a Meaningful Morning Routine Will Make You More Successful
    git图示所有分支的历史
    Rewriting History with Git Rebase
  • 原文地址:https://www.cnblogs.com/dlcode/p/14130214.html
Copyright © 2011-2022 走看看