zoukankan      html  css  js  c++  java
  • 数据结构学习笔记4.5--二叉树效率

    之前的第4部分提到了二叉搜索树的查找,插入,删除操作,那二叉树的效率如何呢?

    在一个满树中,大约有一半的节点在最低层,因此,查找、插入、删除节点的操作大约有一半都需要找到最低层的节点。

    按照满树的计算方法,树的操作复杂度为O(logN)。但是遍历树相对来说要慢上许多,因此,如果不涉及到遍历操作的,二叉搜索树是一种很多好的数据结构。

    二叉树完整代码:包含查找,插入,删除,遍历操作。

    package Charp04.BinaryTree;
    
    import java.util.Stack;
    
    public class Tree
    {
        /*
         * 根节点
         */
        private Node root;
    
        /**
         * 构造函数
         */
        public Tree()
        {
            root = null;
        }
    
        /**
         * 查找节点
         * 
         * @param key 输入key值
         * @return 返回节点值
         */
        public Node find(int key)
        {
            // 当前节点,相当于一个指针,引用会随着查找变化
            Node current = root;
    
            // 遍历查询节点
            // 查找方法:根据二叉树的特点,左子节点值总是比父节点值小,右子节点值总比父节点值大
            while (current.iData != key)
            {
                // 比较当前节点值,
                if (key < current.iData)
                {
                    // 如果值小于key,则将当前节点current引用变化为左子节点,此时不用判断引用是否为null
                    current = current.leftChild;
                }
                else
                {
                    // 如果值大于key,则将当前节点current引用变化为右子节点,此时不用判断引用是否为null
                    current = current.rightChild;
                }
    
                // 如果当前节点为空,则表示无法找到key对应的节点
                if (current == null)
                {
                    return null;
                }
            }
    
            return current;
        }
    
        /**
         * 插入节点,跟查找节点代码类似,只是在遇到null时,将节点插入,修改引用
         * 
         * @param id key值
         * @param dd value值
         */
        public void insert(int id, double dd)
        {
            // 插入的节点
            Node newNode = new Node();
            newNode.iData = id;
            newNode.dData = dd;
    
            // 如果根节点为空,则插入节点为根节点
            if (root == null)
            {
                root = newNode;
            }
            else
            {
                // 当前节点,引用会随在查找变化
                // 由于查找要插入的节点是从根基点开始,所以root引用赋值给current
                Node current = root;
    
                // 父节点
                // 有父节点的引用,才能够给子节点leftChild或者rightChild赋值
                Node parent;
    
                while (true)
                {
                    // 保存父节点的引用,因为后面在查找时,current的引用一定会为null,
                    // 此时说明已经找到插入节点的位置了
                    parent = current;
    
                    // 插入节点在左子树
                    if (id < current.iData)
                    {
                        current = current.leftChild;
                        if (current == null)
                        {
                            parent.leftChild = newNode;
                            return;
                        }
                    }
                    // 插入节点在右子树
                    else
                    {
                        current = current.rightChild;
                        if (current == null)
                        {
                            parent.rightChild = newNode;
                            return;
                        }
                    }
                }
            }
        }
    
        /**
         * 删除节点
         * 
         * @param key 删除节点key值
         * @return 返回值
         */
        public boolean delete(int key)
        {
            Node current = root; // 当前节点
            Node parent = root; // 父节点,用于标记删除节点的父节点
            boolean isLeftChild = true; // 是否是左子节点
    
            // 查找要删除的节点
            while (current.iData != key)
            {
                // 保存父节点的引用
                parent = current;
    
                // 删除节点在左子树
                if (key < current.iData)
                {
                    isLeftChild = true;
                    current = current.leftChild;
                }
                // 删除节点在左子树
                else
                {
                    isLeftChild = false;
                    current = current.rightChild;
                }
    
                // 找不到删除节点,返回
                if (current == null)
                {
                    return false;
                }
            }
    
            // 删除没有子节点的节点
            // 即删除节点为current,此时其左子节点、右子节点的应用都为null
            if (current.leftChild == null && current.rightChild == null)
            {
                if (current == root)
                {
                    root = null;
                }
                else if (isLeftChild)
                {
                    // 修改current父节点左子节点的引用
                    parent.leftChild = null;
                }
                else
                {
                    // 修改current父节点右子节点的引用
                    parent.rightChild = null;
                }
            }
            // 删除节点current的右子节点为空,即只有左子节点
            else if (current.rightChild == null)
            {
                // 判断是否为根
                if (current == root)
                {
                    root = current.leftChild;
                }
                // 如果删除节点为parent的左子节点,则引用赋值为左子树
                else if (isLeftChild)
                {
                    parent.leftChild = current.leftChild;
                }
                // 如果删除节点为parent的右子节点,则引用赋值为右子树
                else
                {
                    parent.rightChild = current.leftChild;
                }
            }
            // 删除节点current的左子节点为空,即只有右子节点
            else if (current.leftChild == null)
            {
                // 判断是否为根
                if (current == root)
                {
                    root = current.rightChild;
                }
                // 如果删除节点为parent的左子节点,则引用赋值为左子树
                else if (isLeftChild)
                {
                    parent.leftChild = current.rightChild;
                }
                // 如果删除节点为parent的右子节点,则引用赋值为右子树
                else
                {
                    parent.rightChild = current.rightChild;
                }
            }
            else
            {
                // 找到后继
                Node successor = getSuccessor(current);
    
                // 判断删除节点是否为根的情形
                if (current == root)
                {
                    root = successor;
                }
                else if (isLeftChild)
                {
                    // 连接删除节点的父节点与后继节点
                    parent.leftChild = successor;
                }
                else
                {
                    // 连接删除节点的父节点与后继节点
                    parent.rightChild = successor;
                }
    
                // 连接后继左子节点
                successor.leftChild = current.leftChild;
            }
    
            return true;
        }
    
        /**
         * 获取后继节点
         * @param delNode 删除节点
         * @return 后继节点
         */
        private Node getSuccessor(Node delNode)
        {
            Node successorParent = delNode; // 存放后继节点的父节点,因为需要断开后继节点,需要保存父节点的引用
            Node successor = delNode; // 存放后继节点
            Node current = delNode.rightChild; // 当前节点
    
            // 循环查找后继节点,最后currnt一定为null
            while (current != null)
            {
                successorParent = successor;
                successor = current;
                current = current.leftChild;
            }
    
            // 后继节点不是右子节点,即沿着左子节点路径寻找的情形
            if (successor != delNode.rightChild)
            {
                // 将后继节点的右子节点(有可能是右子树)的引用赋值给后继的父节点,即连接父节点与孙节点
                // 这样才能将后继节点断开,同时保持后继节点的子节点关系
                successorParent.leftChild = successor.rightChild;
                
                // 将删除节点的右子节点引用赋值给后继节点
                // 由于后继替换到删除节点的位置,因此需要改变删除节点右子节点的连接关系
                successor.rightChild = delNode.rightChild;
            }
    
            return successor;
        }
    
        /**
         * 二叉树遍历
         * 
         * @param traverseType 遍历类型
         */
        public void traverse(int traverseType)
        {
            switch (traverseType)
            {
            case 1:
                // 前序遍历
                System.out.print("
    Preorder traversal:");
                preOrder(root);
                break;
            case 2:
                // 中序遍历
                System.out.print("
    Inorder traversal:");
                inOrder(root);
                break;
            case 3:
                // 后序遍历
                System.out.print("
    Postorder traversal:");
                postOrder(root);
                break;
            default:
                break;
            }
        }
    
        /**
         * 前序遍历
         * 
         * @param localRoot 节点值
         */
        private void preOrder(Node localRoot)
        {
            if (localRoot != null)
            {
                System.out.print(localRoot.iData + " ");
                preOrder(localRoot.leftChild);
                preOrder(localRoot.rightChild);
            }
        }
    
        /**
         * 中序遍历
         * 
         * @param localRoot 节点值
         */
        private void inOrder(Node localRoot)
        {
            if (localRoot != null)
            {
                inOrder(localRoot.leftChild);
                System.out.print(localRoot.iData + " ");
                inOrder(localRoot.rightChild);
            }
        }
    
        /**
         * 后续遍历
         * 
         * @param localRoot 节点值
         */
        private void postOrder(Node localRoot)
        {
            if (localRoot != null)
            {
                postOrder(localRoot.leftChild);
                postOrder(localRoot.rightChild);
                System.out.print(localRoot.iData + " ");
            }
        }
    
        /**
         * 打印二叉树
         */
        public void displayTree()
        {
            Stack<Node> globalStack = new Stack<Node>();
            globalStack.push(root);
            int nBlanks = 32;
            boolean isRowEmpty = false;
            System.out.println("......................................");
    
            while (isRowEmpty == false)
            {
                Stack<Node> localStack = new Stack<Node>();
                isRowEmpty = true;
    
                for (int i = 0; i < nBlanks; i++)
                {
                    System.out.print(' ');
                }
    
                while (globalStack.isEmpty() == false)
                {
                    Node temp = (Node) globalStack.pop();
                    if (temp != null)
                    {
                        System.out.print(temp.iData);
                        localStack.push(temp.leftChild);
                        localStack.push(temp.rightChild);
    
                        if (temp.leftChild != null || temp.rightChild != null)
                        {
                            isRowEmpty = false;
                        }
                    }
                    else
                    {
                        System.out.print("--");
                        localStack.push(null);
                        localStack.push(null);
                    }
    
                    for (int i = 0; i < nBlanks * 2 - 2; i++)
                    {
                        System.out.print(' ');
                    }
                }
    
                System.out.println();
                nBlanks /= 2;
                while (localStack.isEmpty() == false)
                {
                    globalStack.push(localStack.pop());
                }
            }
    
            System.out.println("......................................");
        }
    }
  • 相关阅读:
    优化后的 google提供的汉字转拼音类(针对某些htc等手机的不兼容情况)
    运行期可以变动大小和尺寸的自定义控件、
    123
    动态创建Ⅱ
    动态创建Ⅰ
    delphi XE5皮肤的使用
    ActionBar
    zhizhensuibi---Source aplikasi database dengan delphi7
    chadang saidui
    30yong wanquan
  • 原文地址:https://www.cnblogs.com/winlrou/p/3547403.html
Copyright © 2011-2022 走看看