二叉查找树,其深度的平均值是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();
}
}