二叉查找树(Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
二叉查找树的表示:
class Node {
int data;
Node leftChild = null;
Node rightChild = null;
}
二叉查找树的常用方法:
1. 查找(lookup)
/*
Given a binary tree, return true if a node with the target data is found in the tree. Recurs down the tree, chooses the left or right branch by comparing the target to each node.
*/
static boolean lookup(Node rootNode, int target) {
// 1. Base case == empty tree
// in that case, the target is not found so return false
if (rootNode == null) return false;
else if (target == rootNode.data) return true;
else if (target < rootNode.data) return(lookup(rootNode.leftChild, target));
else return(lookup(rootNode.rightChild, target));
}
2. 最大(max)
/*
return the node with maximum key value. It should be last right child of the tree, or the root if the root does not have any child
*/
public Node maxNode(Node rootNode) {
while (rootNode.righttChild != null) {
rootNode = rootNode.rightChild;
}
return rootNode;
}
3. 最小 (min)
/*
return the node with minimum key value. It should be last left child of the tree, or the root if the root does not have any child
*/
public Node minNode(Node rootNode) {
while (rootNode.leftChild != null) {
rootNode = rootNode.leftChild;
}
return rootNode;
}
public static Node insert(Node root, int data) {
// 1. If the tree is empty, the new node is the root
if (root == null) {
return new Node(data);
}
else {
// 2. Otherwise, recur down the tree
if (data <= root.data) root.leftChild = insert(root.leftChild, data);
else root.rightChild = insert(root.rightChild, data);
}
return root;
}
5. in-order tree walk
public void inorder(Node rootNode) {
if (rootNode != null) {
inorder(rootNode.leftChild);
print(nodeNode.data);
inorder(rootNode.rightChild);
}
}
6. successor and predecessor
一个node的successor可以通过in-order walk看出来,因为in-order walk实际上是把二叉查找树做了一个排序。 predecesor 刚好和successor 相反。
Search consists of two cases.
1) If node x has a non-empty right subtree, then x’s successor is the minimum in the right subtree of x.
2) If node x has an empty right subtree, then as long as we move to the left up the tree (move up through right children), we are visiting smaller keys. x’s successor y is the node that x is the predecessor of (x is the maximum in y’s left subtree). In other words, x’s successor y, is the lowest ancestor of x whose left child is also an ancestor of x.
public Node Successor(Node node) {
if (node.rightChild != null) {
return minNode(node.rightChild);
}
Node parentNode = node.parent;
while (parentNode != null && node == parentNode.rightChild) {
node = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
}
If node has two children, its predecessor is the maximum value in its left subtree. If it does not have a left child a node's predecessor is its first left ancestor.
public Node Successor(Node node) {
if (node.rightChild != null) {
return minNode(node.rightChild);
}
Node parentNode = node.parent;
while (parentNode != null && node == parentNode.leftChild) {
node = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
}
7. 删除 (delete)
case 1: if x has no children, then remove x;
case 2: if x has one child, then make p[x] point to child;
case 3: if x has two children (subtrees) , then swap x with its successor, perform case 0 or case 1 to delete it.
伪代码:
delete(x)
if x.left = NIL and x.right = NIL //case 1
if x.parent.left = x
x.parent.left = NIL
else
x.parent.right = NIL
else if x.left = NIL //case 2a
connect x.parent to x.right
else if x.right = NIL //case 2b
connect x.parent to x.left
else //case 3
y = successor(x)
connect y.parent to y.right
replace x with y
/*
Compute the number of nodes in a tree.
*/
int size(Node rootNode) {
if (node == NULL) {
return(0);
} else {
return(size(rootNode.leftChild) + 1 + size(rootNode.rightChild));
}
}
9. 把一个二叉树转成它的“镜像”(mirror)
private void mirror(Node rootNode) {
if (rootNode != null) {
// do the sub-trees
mirror(rootNode.leftChild);
mirror(rootNode.rightChild);
// swap the left/right pointers
Node temp = rootNode.leftChild;
rootNode.leftChild = rootNode.rightChild;
rootNode.rightChild = temp;
}
}
10. check balanced (isBalanced)
To check a binary tree is balanced or not, we can simply convert this question into comparing the maximum depth and minimum depth of the BT, if the diffrence between the maximum depth and minimum depth is larger than 1, the tree is not balanced, otherwise, it is balanced.
int maxDepth(Node rootNode) {
if (rootNode == null) {
return 0;
}
return 1 + max(maxDepth(rootNode.leftChild), maxDepth(rootNode.rightChild));
}
int minDepth(Node rootNode) {
if (rootNode == null) {
return 0;
}
return 1 + min(minDepth(rootNode.leftChild), minDepth(rootNode.rightChild));
}
bool isBalanced(Node rootNode) {
return maxDepth(rootNode) - minDepth(rootNode) <= 1;
}
11. First common ancestor
查找两个node的最早的公共祖先,分三种情况:
1. 如果两个node在root的两边,那么最早的公共祖先就是root。
2. 如果两个node在root的左边,那么把root.leftChild作为root,再递归。
3. 如果两个node在root的右边,那么把root.rightChild作为root,再递归。
/*
* get the first common ancestor of node p and node q
*/
public static Node commonAncestor(Node rootNode, Node p, Node q) {
if (covers(rootNode.leftChild, p) && covers(rootNode.leftChild, q))
return commonAncestor(rootNode.leftChild, p, q);
if (covers(rootNode.rightChild, p) && covers(rootNode.rightChild, q))
return commonAncestor(rootNode.rightChild, p, q);
return rootNode;
}
/*
* check whether the node n is in the tree
*/
private static boolean covers(Node rootNode, Node n) {
if(rootNode == null) return false;
if(rootNode == n) return true;
return covers(rootNode.leftChild, n) || covers(rootNode.rightChild, n);
}
另一种做法,复杂度更低。
http://www.ihas1337code.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html
如果这个Binary Tree 是 BST的话,可以利用左子树的值小于根节点的值,右子树的值大于根节点的值进行判断两个节点(p, q)的位置,这样更简单。
public Node LCA(Node root, Node p, Node q) {
if (root == null || p == null || q == null ) return NULL;
if (max(p.data, q.data) < root.data)
return LCA(root.left, p, q);
else if (min(p.data, q.data) > root.data)
return LCA(root.right, p, q);
else
return root;
}
这个算法的复杂度是O(h)。
12. ISBST()
Given a plain binary tree, examine the tree to determine if it meets the requirement to be a binary search tree. To be a binary search tree, for every node, all of the nodes in its left tree must be <= the node, and all of the nodes in its right subtree must be > the node.
private boolean isBST(Node node) {
if (node==null) return(true);
if (node.left!=null && maxValue(node.left) > node.data) return(false);
if (node.right!=null && minValue(node.right) <= node.data) return(false);
// check that the subtrees themselves are ok
return( isBST(node.left) && isBST(node.right) );
}
However, the approach given above is very expensive (O(n*h)), because the maxValue(Node node) function will be called n times, and the complexity of the maxValue(Node node) is h (the height of the tree).
Because of the properity of BST, we can simply pass down the minvalue and maxvalue after narrowing the range of the minvalue and maxvalue, and the complexity of this algorithm is O(n). The code below shows the algorithm.
bool isBSTHelper(Node p, int low, int high) {
if (p == null) return true;
if (low < p.data && p.data < high)
return isBSTHelper(p.leftChild, low, p.data) &&
isBSTHelper(p.rightChild, p.data, high);
else
return false;
}
bool isBST(Node root) {
return isBSTHelper(root, INT_MIN, INT_MAX);
}
参考: http://cslibrary.stanford.edu/110/BinaryTrees.html