第4章 树


二叉查找树,其深度的平均值是O(logN)。查找、插入、删除平均时间O(logN)。

如果删除的次数不多,通常使用的策略是懒惰删除:当一个元素要被删除时,它仍留在书中,而只是被标记为删除。 

AVL树是带有平衡条件的二叉查找树。它保证树的深度须是O(logN)。一棵AVL树是其每个节点的左子树和右子数的高度最多差1的二叉查找树。 

伸展树,它保证从空树开始连续M次对树的操作最多花费O(MlogN)时间。伸展树基于这样的事实:对于二叉查找树来说,每次操作最坏情况时间O(N)并不坏,只要它相对不常发生就行。伸展树的基本想法是,当一个节点被访问后,它就要经过一系列AVL树的旋转被推到根上。 

一个完全二叉树的高度大约为log2N,而一个完全M叉树的高度大约是logMN。 
B树有深度O(logM/2N)。 

TreeSet和TreeMap使用自顶向下的红黑树实现。 


二叉查找树代码:

package com.datastructures.solutionsmanual;

/**
 * 二叉查找树: 对于树中的每个节点x,它的左子树中所有向的值小于x,而它的右子树中所有项的值大于x
 * 
 * @author user
 * @param <E>
 */
public class BinarySearchTree<E extends Comparable<? super E>> {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

    private static class BinaryNode<E> {

        E element;// The data int the node
        BinaryNode<E> left;// Left child
        BinaryNode<E> right;// Right child

        // Constructors
        BinaryNode(E theElement) {
            this(theElement, null, null);
        }

        BinaryNode(E theElement, BinaryNode<E> lt, BinaryNode<E> rt) {
            element = theElement;
            left = lt;
            right = rt;
        }

    }

    private BinaryNode<E> root;

    // private Comparable<? super E> cmp;

    public BinarySearchTree() {
        root = null;
    }

    public BinarySearchTree(Comparable<? super E> c) {
        root = null;
        // cmp = c ;
    }

    public void makeEmpty() {
        root = null;
    }

    public boolean isEmpty() {
        return root == null;
    }

    public boolean contains(E x) {
        return contains(x, root);
    }

    public E findMin() {
        if (isEmpty())
            return null;
        return findMin(root).element;
    }

    public E findMax() {
        if (isEmpty())
            return null;
        return findMax(root).element;
    }

    private void insert(E x) {
        root = insert(x, root);
    }

    private void remove(E x) {
        root = remove(x, root);
    }

    /**
     * Print the tree contents in sorted order
     */
    private void printTree() {
        if (isEmpty()) {
            System.out.println("Empty tree");
        }
        printTree(root);
    }

    /**
     * Internal method to find an item in a subtree
     * 
     * @param x
     * @param t
     * @return
     */
    private boolean contains(E x, BinaryNode<E> t) {
        if (t == null)
            return false;
        int compareResult = x.compareTo(t.element);
        if (compareResult < 0) {
            return contains(x, t.left);
        } else if (compareResult > 0) {
            return contains(x, t.right);
        } else {
            return true;
        }
    }

    /**
     * Internal method to find the smallest item in a subtree
     * 
     * @param t
     * @return
     */
    private BinaryNode<E> findMin(BinaryNode<E> t) {
        if (t == null)
            return null;
        else if (t.left == null)
            return t;
        return findMin(t.left);
    }

    /**
     * Internal method to find the largest item in a subtree
     * 
     * @param t
     * @return
     */
    private BinaryNode<E> findMax(BinaryNode<E> t) {
        if (t != null) {
            while (t.right != null) {
                t = t.right;
            }
        }
        return t;
    }

    /**
     * Internal method to insert into a subtree
     * 
     * @param x
     * @param t
     * @return
     */
    private BinaryNode<E> insert(E x, BinaryNode<E> t) {
        if (t == null)
            return new BinaryNode<E>(x);
        int compareResult = x.compareTo(t.element);
        if (compareResult < 0) {
            t.left = insert(x, t.left);
        } else if (compareResult > 0) {
            t.right = insert(x, t.right);
        }
        return t;
    }

    private void insert2(E x, BinaryNode<E> t) {
        if (t == null)
            return;
        int compareResult = (x).compareTo(t.element);
        if (compareResult < 0) {
            if (t.left == null) {
                t.left = new BinaryNode<E>(x);
            } else {
                insert2(x, t.left);
            }
        } else if (compareResult > 0) {
            if (t.right == null) {
                t.right = new BinaryNode<E>(x);
            } else {
                insert2(x, t.right);
            }
        }
    }

    /**
     * Internal method to remove from a subtree
     * 
     * @param x
     * @param t
     * @return
     */
    private BinaryNode<E> remove(E x, BinaryNode<E> t) {
        if (t == null)
            return t;
        int compareResult = x.compareTo(t.element);
        if (compareResult < 0) {
            t.left = remove(x, t.left);
        } else if (compareResult > 0) {
            t.right = remove(x, t.right);
        } else if (t.left != null && t.right != null) {// two children
            t.element = findMin(t.right).element;// 查找右子树中最小的节点//这会导致左沉右轻
            t.right = remove(t.element, t.right);// 删除右子树中最小节点
                                                 // //也可以查找和删除左子树的最大节点
        } else {
            t = (t.left != null) ? t.left : t.right;
        }
        return t;
    }

    /**
     * Internal method to print a suntree in sorted order.
     * 
     * @param t
     */
    private void printTree(BinaryNode<E> t) {
        if (t != null) {
            printTree(t.left);
            System.out.println(t.element);
            printTree(t.right);
        }
    }
}

AVL树:

package com.datastructures.solutionsmanual;

/**
 * AVL(Adelson-Velskii和Landis) 数是带有平衡条件的二叉查找树。 左子树和右子树的高度最多差1
 * 
 * @author user
 * @param <E>
 */
public class AvlNode<E extends Comparable<? super E>> {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

    E element;
    AvlNode<E> left;
    AvlNode<E> right;
    int height;

    AvlNode(E element) {
        this(element, null, null);
    }

    AvlNode(E theElement, AvlNode<E> lt, AvlNode<E> rt) {
        element = theElement;
        left = lt;
        right = rt;
        height = 0;
    }

    private int height(AvlNode<E> t) {
        return t == null ? -1 : t.height;
    }

    /**
     * Internal method to insert into a subtree
     * 
     * @param x the item to insert
     * @param t the node that roots the subtree
     * @return the new root of the subtree
     */
    private AvlNode<E> insert(E x, AvlNode<E> t) {
        if (t == null)
            return new AvlNode<E>(x);
        int comparResult = x.compareTo(t.element);
        if (comparResult < 0) {// 比值小,左侧递归插入
            t.left = insert(x, t.left);// 先进行一般性递归插入
            if (height(t.left) - height(t.right) == 2) {// 然后比较
                if (x.compareTo(t.left.element) < 0) {// 左左单旋转否则是双旋转
                    t = rotateWithLeftChild(t);
                } else {
                    t = doubleWithLeftChild(t);
                }
            }
        } else if (comparResult > 0) {// 比值大,右侧递归插入
            t.right = insert(x, t.right);
            if (height(t.right) - height(t.left) == 2) {
                if (x.compareTo(t.right.element) < 0) {// 右右单旋转否则双旋转
                    t = rotateWithRightChild(t);
                } else {
                    t = doubleWithRightChild(t);
                }
            }
        }
        t.height = Math.max(height(t.left), height(t.right)) + 1;
        return t;
    }

    /**
     * Rotate binary tree node with left child.For AVL trees,this is a single
     * rotation for case1 Update heights,then return new root. <code>
         k2                                k1             
         /   \                            /      \          
        k1   z       ==>          x         k2      
       /   \                                      /   \     
      x   y                                    y      z   
</code>
     * 
     * @param k2
     * @return
     */
    private AvlNode<E> rotateWithLeftChild(AvlNode<E> k2) {
        AvlNode<E> k1 = k2.left;// 取值k2.left,寻找k1
        k2.left = k1.right;// 重新赋值k2.left取值于k1.right,k2整个树固定
        k1.right = k2;// 重新赋值k1.right,对接k1、k2
        k2.height = Math.max(height(k2.left), height(k2.right)) + 1;// 这里如果针对AVL树,感觉height可不可以简单的-1?
        k1.height = Math.max(height(k1.left), k2.height) + 1;
        return k1;
    }

    /**
     * <code>
         k2                                k1             
         /   \                            /      \          
        x   k1       ==>          k2       z
           /   \                       /   \     
          y     z                    x      y   
</code>
     */
    private AvlNode<E> rotateWithRightChild(AvlNode<E> k2) {
        AvlNode<E> k1 = k2.right;// 取值k2.left,寻找k1
        k2.right = k1.left;// 重新赋值k2.left取值于k1.right,k2整个树固定
        k1.left = k2;// 重新赋值k1.right,对接k1、k2
        k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
        k1.height = Math.max(height(k1.right), k2.height) + 1;
        return k1;
    }

    /**
     * Double rotate binary tree node:first left child with its right child;then
     * node k3 with new left child For AVL trees,this is a double rotation for
     * case2 . Update heights,then return new root. <code>
           k3                                    k3                                                        k2                    
         /   \                                   /   \                                                      /      \                  
        k1   d                             k2     d                                                k1       k3             
       /   \                ==>          /  \                      ==>                         /   \      /   \            
     a      k2                           k1  c                                                  a      b  c      d           
            /   \                          /   \                                                                                         
          b      c                      a      b                                                                                   
</code>
     * 
     * @param k3
     * @return
     */
    private AvlNode<E> doubleWithLeftChild(AvlNode<E> k3) {
        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);
    }

    private AvlNode<E> doubleRotateWithLeft(AvlNode k3) {
        AvlNode k1, k2;
        k1 = k3.left;
        k2 = k1.right;
        
        k1.right = k2.left;
        k3.left = k2.right;
        k2.left = k1;
        k2.right = k3;
        
        k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
        k3.height = Math.max(height(k3.left), height(k3.right)) + 1;
        k1.height = Math.max(k1.height, k3.height) + 1;

        return k3;
    }

    private AvlNode<E> doubleWithRightChild(AvlNode<E> k3) {
        k3.right = rotateWithRightChild(k3.right);
        return rotateWithLeftChild(k3);
    }

}

4.10 列出一个目录中所有的文件和它们的大小:

  private static void forPrintFile(File f, int dep) {
        for (int i = 0; i < dep; i++) {
            System.out.print("    ");
            writeFile("     ");
        }
        if (f.isDirectory()) {
            System.out.println("<" + f.getName() + ">");
            File[] files = f.listFiles();
            for (File file : files) {
                forPrintFile(file, dep + 1);
            }
            System.out.println("</" + f.getName() + ">");
        } else {
            System.out.println("<" + f.getName() + ">" + "lengh:" + f.length() + "</" + f.getName()
                    + ">");
        }
    }

顺便记述一下文件的读写:

    /**
     * FileWritter, 字符流写入字符到文件。默认情况下,它会使用新的内容取代所有现有的内容, 然而,当指定一个true
     * (布尔)值作为FileWritter构造函数的第二个参数,它会保留现有的内容, 并追加新内容在文件的末尾。
     * 
     * @param str
     */
    private static void writeFile(String str) {
        boolean dbg = false;
        if (!dbg)
            return;
        try {
            File file = new File("/home/user/lisx/test/tes.xml");
            // if file doesnt exists, then create it
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWritter = new FileWriter(file, true);
            BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
            bufferWritter.write(str);
            bufferWritter.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。
     */
    public static void readFileByBytes(String fileName) {
        File file = new File(fileName);
        InputStream in = null;
        try {
            System.out.println("以字节为单位读取文件内容,一次读一个字节:");
            // 一次读一个字节
            in = new FileInputStream(file);
            int tempbyte;
            while ((tempbyte = in.read()) != -1) {
                System.out.write(tempbyte);
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            System.out.println("以字节为单位读取文件内容,一次读多个字节:");
            // 一次读多个字节
            byte[] tempbytes = new byte[100];
            int byteread = 0;
            in = new FileInputStream(fileName);
            showAvailableBytes(in);
            // 读入多个字节到字节数组中,byteread为一次读入的字节数
            while ((byteread = in.read(tempbytes)) != -1) {
                System.out.write(tempbytes, 0, byteread);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * 以字符为单位读取文件,常用于读文本,数字等类型的文件
     */
    public static void readFileByChars(String fileName) {
        File file = new File(fileName);
        Reader reader = null;
        try {
            System.out.println("以字符为单位读取文件内容,一次读一个字节:");
            // 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file));
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                // 对于windows下,\r\n这两个字符在一起时,表示一个换行。
                // 但如果这两个字符分开显示时,会换两次行。
                // 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
                if (((char) tempchar) != '\r') {
                    System.out.print((char) tempchar);
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("以字符为单位读取文件内容,一次读多个字节:");
            // 一次读多个字符
            char[] tempchars = new char[30];
            int charread = 0;
            reader = new InputStreamReader(new FileInputStream(fileName));
            // 读入多个字符到字符数组中,charread为一次读取字符数
            while ((charread = reader.read(tempchars)) != -1) {
                // 同样屏蔽掉\r不显示
                if ((charread == tempchars.length)
                        && (tempchars[tempchars.length - 1] != '\r')) {
                    System.out.print(tempchars);
                } else {
                    for (int i = 0; i < charread; i++) {
                        if (tempchars[i] == '\r') {
                            continue;
                        } else {
                            System.out.print(tempchars[i]);
                        }
                    }
                }
            }

        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * 以行为单位读取文件,常用于读面向行的格式化文件
     */
    public static void readFileByLines(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            System.out.println("以行为单位读取文件内容,一次读一整行:");
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 1;
            // 一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                // 显示行号
                System.out.println("line " + line + ": " + tempString);
                line++;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * 随机读取文件内容
     */
    public static void readFileByRandomAccess(String fileName) {
        RandomAccessFile randomFile = null;
        try {
            System.out.println("随机读取一段文件内容:");
            // 打开一个随机访问文件流,按只读方式
            randomFile = new RandomAccessFile(fileName, "r");
            // 文件长度,字节数
            long fileLength = randomFile.length();
            // 读文件的起始位置
            int beginIndex = (fileLength > 4) ? 4 : 0;
            // 将读文件的开始位置移到beginIndex位置。
            randomFile.seek(beginIndex);
            byte[] bytes = new byte[10];
            int byteread = 0;
            // 一次读10个字节,如果文件内容不足10个字节,则读剩下的字节。
            // 将一次读取的字节数赋给byteread
            while ((byteread = randomFile.read(bytes)) != -1) {
                System.out.write(bytes, 0, byteread);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (randomFile != null) {
                try {
                    randomFile.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * 显示输入流中还剩的字节数
     */
    private static void showAvailableBytes(InputStream in) {
        try {
            System.out.println("当前字节输入流中的字节数为:" + in.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值