二叉查找树简介
二叉查找树(Binary Search Tree),又被称为二叉搜索树。
它是特殊的二叉树:对于二叉树,假设x为二叉树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。那么,这棵树就是二叉查找树。如下图所示:
在二叉查找树中:
(1) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3) 任意节点的左、右子树也分别为二叉查找树。
(4) 没有键值相等的节点(no duplicate nodes)。
二叉查找树的实现
1.二叉查找树节点实现
public class BinaryNode<T> { private T element; private BinaryNode<T> left; private BinaryNode<T> right; public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) { this.element = element; this.left = left; this.right = right; } public BinaryNode(T element) { this(element, null, null); }}
(1) element–当前节点的值
(2) right – 它指向当前节点的右孩子。
(3) parent – 它指向当前节点的父结点。
2.contains方法的实现
如果在树T中存在含有项X的节点,那么contains方法返回true,否则返回false.如果树T是空集,则返回false.
public boolean contains(T t, BinaryNode<T> node) { if (t == null) { return false; } int campareResult = t.compareTo(node.element); if (campareResult < 0) { return contains(t, node.left); } else if (campareResult > 0) { return contains(t, node.right); } else { return true; } }
从根节点开始查找该值,如果根节点匹配,则直接返回true,否则和根节点的值进行比较,然后递归遍历左子树或右子树进行查找。
3.findMin方法和findMax方法
public T findMax() { if (isEmpty()) { throw new IndexOutOfBoundsException(); } return findMax(mRoot).element; } private BinaryNode<T> findMax(BinaryNode<T> node) { if (node != null) { while (node.right != null) { node = node.right; } } return node; }
对于findMax方法,如果存在右子树,则递归遍历,终止点就是最大的元素。否则,根节点就是最大的元素。
public T findMin(){ if (isEmpty()) { throw new IndexOutOfBoundsException(); } return findMin(mRoot).element; } private BinaryNode<T> findMin(BinaryNode<T> node) { if (node != null) { while (node.left != null) { node = node.left; } } return node; }
对于findMin方法,如果存在左子树,则递归遍历,终止点就是最小的元素。否则,根节点就是最小的元素。
4.insert方法
将一个新的元素X插入到树T中,可以先通过contains方法去查找该元素是否存在,如果存在,则什么都不做,否则将X插入到遍历路径上的最后一点。
public void insert(T t) { mRoot = insert(t, mRoot); } public BinaryNode<T> insert(T t, BinaryNode<T> node) { if (node == null) { return new BinaryNode<T>(t, null, null); } int compareResult = t.compareTo(node.element); if (compareResult > 0) { node.right = insert(t, node.right); } else if (compareResult < 0) { node.left = insert(t, node.left); } return node; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
5.remove方法
public void remove(T t) { mRoot = remove(t, mRoot); } public BinaryNode<T> remove(T t, BinaryNode<T> node) { if (node == null) { return node; } int compareResult = t.compareTo(node.element); if (compareResult > 0) { node.right = remove(t, node.right); } else if (compareResult < 0) { node.left = remove(t, node.left); } else if (node.left != null && node.right != null) { node.element = findMin(node.right).element; node.right = remove(node.element, node.right); } else { node = (node.left != null) ? node.left : node.right; } return node; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
对于删除,最复杂的是该将被删除的节点A具有两个儿子的情况,一般是该节点的右子树最小的节点B代替节点A,然后在B原来的位置删除掉节点B。
上面的代码能够完成删除工作,但效率并不高,因为它沿着树进行两趟搜索来查找和删除右子树中最小的节点。
如果删除的次数不多,通常使用懒惰删除:当一个元素要被删除时,它仍在树中,而只是标记为删除。
特别是删除算法会使得左子树比右子树深,因为我们总是用右子树的一个来代替删除的节点。会造成二叉查找树,严重的不平衡。
5.二叉查找树完整代码
public class BinarySearchTree<T extends Comparable<? super T>> { private BinaryNode<T> mRoot; public void makeEmpty() { mRoot = null; } public boolean isEmpty() { return mRoot == null; } public boolean contains(T t) { return contains(t, mRoot); } public boolean contains(T t, BinaryNode<T> node) { if (t == null) { return false; } int campareResult = t.compareTo(node.element); if (campareResult < 0) { return contains(t, node.left); } else if (campareResult > 0) { return contains(t, node.right); } else { return true; } } public T findMax() { if (isEmpty()) { throw new IndexOutOfBoundsException(); } return findMax(mRoot).element; } private BinaryNode<T> findMax(BinaryNode<T> node) { if (node != null) { while (node.right != null) { node = node.right; } } return node; } public T findMin() { if (isEmpty()) { throw new IndexOutOfBoundsException(); } return findMin(mRoot).element; } private BinaryNode<T> findMin(BinaryNode<T> node) { if (node != null) { while (node.left != null) { node = node.left; } } return node; } public void insert(T t) { mRoot = insert(t, mRoot); } public BinaryNode<T> insert(T t, BinaryNode<T> node) { if (node == null) { return new BinaryNode<T>(t, null, null); } int compareResult = t.compareTo(node.element); if (compareResult > 0) { node.right = insert(t, node.right); } else if (compareResult < 0) { node.left = insert(t, node.left); } return node; } public void remove(T t) { mRoot = remove(t, mRoot); } public BinaryNode<T> remove(T t, BinaryNode<T> node) { if (node == null) { return node; } int compareResult = t.compareTo(node.element); if (compareResult > 0) { node.right = remove(t, node.right); } else if (compareResult < 0) { node.left = remove(t, node.left); } else if (node.left != null && node.right != null) { node.element = findMin(node.right).element; node.right = remove(node.element, node.right); } else { node = (node.left != null) ? node.left : node.right; } return node; } private static class BinaryNode<T> { private T element; private BinaryNode<T> left; private BinaryNode<T> right; public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) { this.element = element; this.left = left; this.right = right; } public BinaryNode(T element) { this(element, null, null); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
二叉树的遍历
1.前序遍历
若二叉树非空,则执行以下操作:
(1) 访问根结点;
(2) 先序遍历左子树;
(3) 先序遍历右子树。
前序遍历代码
private void preOrder(BinaryNode<T> tree) { if(tree != null) { System.out.print(tree.element+" "); preOrder(tree.left); preOrder(tree.right); } } public void preOrder() { preOrder(mRoot); }
2.中序遍历
若二叉树非空,则执行以下操作:
(1) 中序遍历左子树;
(2) 访问根结点;
(3) 中序遍历右子树。
中序遍历代码
private void inOrder(BinaryNode<T> tree) { if(tree != null) { inOrder(tree.left); System.out.print(tree.element+" "); inOrder(tree.right); } } public void inOrder() { inOrder(mRoot); }
3.后序遍历
若二叉树非空,则执行以下操作:
(1) 后序遍历左子树;
(2) 后序遍历右子树;
(3) 访问根结点。
后序遍历代码
private void postOrder(BinaryNode<T> tree) { if(tree != null) { postOrder(tree.left); postOrder(tree.right); System.out.print(tree.element+" "); } } public void postOrder() { postOrder(mRoot); }