说到二叉搜索树,就让我想到了本科时期大寒假写代码的那段时期。大冷天没空调一个人拿着笔记本在楼下客厅照着书敲代码,当时敲的就是二叉搜索树,老爸让我快上楼睡觉,我非不肯,因为代码没看懂。就是那个晚上,我不幸感冒了,所以和二叉搜索树还是有很深的感觉的,哈哈哈。
二叉搜索树和前面的堆结构一样,都有一个重要的规则。它的规则是一个节点的左子树上面的元素一定不大于自己,右子树上面的元素一定不小于自己。就是这么个简单的规则,为我们提供了很多相当有趣的性质。
先来看一副二叉搜索树的图:
可以看到上面的节点值完全符合我们的要求,用公式表示为:leftchild.key <= key < rightchild.key。
这里特别强调一下,二叉搜索树不一定是完全二叉树,有可能是一条直线。
(补充:满二叉树数是二叉树已饱和,不能在固定的层上再添加元素,完全二叉树是只有最后一层为满)
数的遍历有前序遍历,中序遍历,后序遍历。这三种的区别是输出节点的顺序不一样。
1)前序遍历是先输出自己,再遍历左子树,最后遍历右子树
2)中序遍历是先输出左子树,再输出自己,最后遍历右子树
3)后序遍历是先输出左子树,再输出右子树,最后输出自己
中序遍历是可以完全顺序输出的,即从小到大输出。
一般我们已递归的方式完成遍历:
private void preorder(TreeNode tNode)
{
if (tNode != null) {
preorder(tNode.getlChild());
System.out.println(tNode);
preorder(tNode.getrChild());
}
}</span>
其实也可以不用递归的方式,可以借助栈结构,将自身压入栈中,知道左子树尽头,然后出栈,输出自己,再处理右子树,压栈循环。。。。
private void preorder(TreeNode rootNode)
{
Stack<TreeNode> nodeStack = new Stack<>();
TreeNode tNode = rootNode;
while (tNode != null || !nodeStack.empty()) {
if (tNode == null) {
tNode = nodeStack.pop();
System.out.println(tNode);
tNode = tNode.getrChild();
} else {
nodeStack.push(tNode);
tNode = tNode.getlChild();
}
}
}</span>
其他的二种遍历方式都是大同小异。
二叉搜索树可以完成很多种动作: insert, delete, search, findMin, findMax, successor(后继节点), predecessor(前继节点)。
这些动作里面除了delete情况比较复杂,其他的都很简单。下面我们来着重说一下delete的过程:
1)如果该tnode节点是叶子节点,那么用null取代tnode
2)如果该tnode节点只有一个孩子,那么用这个孩子取代tnode
3)如果该tnode节点有两个孩子,那么找tnode节点的后继节点,然后用后继节点替代它。这里有两种情况处理方式不一样,如果后继节点是tnode的右孩子,那么直接替换;如果不是,那么用后继节点的右孩子取代它(因为后继节点左孩子为null),然后再用后继节点取代tnode。
对于第三种情况,我们也可以采用递归的方式解决,不管后继节点是不是tnode的孩子节点,我们先递归delete后继节点,然后用后继节点取代tnode就可以,这种方式代码很简洁也很好理解,值得推荐。
先写个移植子树的函数:
private void transplant(TreeNode uNode, TreeNode vNode)
{
if (uNode == null)
root = vNode;
else {
if (uNode.getParent() == null)
root = vNode;
else if (uNode == uNode.getParent().getlChild())
uNode.getParent().setlChild(vNode);
else {
uNode.getParent().setrChild(vNode);
}
if (vNode != null)
vNode.setParent(uNode.getParent());
}
}</span>
然后我们来分别实现一下这两种方法。
老方法(非递归):
private void delete(TreeNode tNode)
{
if (tNode.getlChild() == null)
transplant(tNode, tNode.getrChild());
else if (tNode.getrChild() == null)
transplant(tNode, tNode.getlChild());
else {
TreeNode replaceNode = successor(tNode);
if (replaceNode != tNode.getrChild()) {
transplant(replaceNode, replaceNode.getrChild());
//处理replaceNode的右孩子
replaceNode.setrChild(tNode.getrChild());
replaceNode.getrChild().setParent(replaceNode);
}
transplant(tNode, replaceNode);
//处理replaceNode的左孩子
replaceNode.setlChild(tNode.getlChild());
replaceNode.getlChild().setParent(replaceNode);
}
}</span>
推荐方法(递归):
private void delete(TreeNode tNode)
{
if (tNode.getlChild() == null)
transplant(tNode, tNode.getrChild());
else if (tNode.getrChild() == null)
transplant(tNode, tNode.getlChild());
else {
TreeNode replaceNode = successor(tNode);
delete(replaceNode);
tNode.setKey(replaceNode.getKey());
}
}</span>
看看同样的时间复杂度,代码量是不是小很多,而且逻辑简单清晰很多。我们可以知道递归最多也就2层深度,因为后继节点无左子树。
下面完全的二叉搜索树代码:
*
*/
package binaryTree;
import java.util.Stack;
import sun.reflect.generics.tree.Tree;
/**
* @author freestyle458
*
*/
public class BinarySearchTree {
private TreeNode root;
public static class TreeNode {
private int key;
private TreeNode lChild;
private TreeNode rChild;
private TreeNode parent;
public TreeNode()
{
key = 0;
lChild = null;
rChild = null;
parent = null;
}
public TreeNode(int value)
{
key = value;
lChild = rChild = parent = null;
}
public int getKey()
{
return key;
}
public void setKey(int key)
{
this.key = key;
}
public TreeNode getlChild()
{
return lChild;
}
public void setlChild(TreeNode lChild)
{
this.lChild = lChild;
}
public TreeNode getrChild()
{
return rChild;
}
public void setrChild(TreeNode rChild)
{
this.rChild = rChild;
}
public TreeNode getParent()
{
return parent;
}
public void setParent(TreeNode parent)
{
this.parent = parent;
}
@Override
public String toString()
{
// TODO Auto-generated method stub
String result = "";
if (lChild == null)
result += "[leftChild:null, ";
else
result += "[leftChild:" + lChild.getKey() + ", ";
result += "key:" + key + ", ";
if (parent != null)
result += "parent:" + parent.getKey() + ", ";
else
result += "parent:null, ";
if (rChild == null)
result += "rightChild:null]";
else
result += "rightChild:" + rChild.getKey() + "]";
return result;
}
}
//lchild.key <= key < rchild.key
public BinarySearchTree()
{
// TODO Auto-generated constructor stub
root = null;
}
public BinarySearchTree(int key)
{
TreeNode node = new TreeNode(key);
root = node;
}
public boolean isContain(int value)
{
return isContain(root, value);
}
/* private boolean isContain(TreeNode t, int value)
{
if (t == null)
return false;
else {
if (t.getKey() == value)
return true;
else if (t.getKey() < value)
return isContain(t.getrChild(), value);
else {
return isContain(t.getlChild(), value);
}
}
}*/
private boolean isContain(TreeNode rootNode, int value)
{
if (search(rootNode, value) == null)
return false;
else
return true;
}
private TreeNode search(TreeNode rootNode, int value)
{
TreeNode xNode = rootNode;
while (xNode != null && xNode.getKey() != value) {
if (xNode.getKey() < value)
xNode = xNode.getrChild();
else
xNode = xNode.getlChild();
}
return xNode;
}
public int findMin()
{
TreeNode tNode = findMin(root);
if (tNode == null)
return -1;
else
return tNode.getKey();
}
private TreeNode findMin(TreeNode tNode)
{
while (tNode.getlChild() != null) {
tNode = tNode.getlChild();
}
return tNode;
}
public int findMax()
{
TreeNode tNode = findMax(root);
if (tNode == null)
return -1;
else
return tNode.getKey();
}
private TreeNode findMax(TreeNode tNode)
{
while (tNode.getrChild() != null)
tNode = tNode.getrChild();
return tNode;
}
private TreeNode successor(TreeNode tNode)
{
if (tNode.getrChild() != null)
return findMin(tNode.getrChild());
else {
TreeNode parent = tNode.getParent();
while (parent != null && tNode == parent.getrChild()) {
tNode = parent;
parent = tNode.getParent();
}
return parent;
}
}
private TreeNode predecessor(TreeNode tNode)
{
if (tNode.getlChild() != null)
return tNode.getlChild();
else {
TreeNode parent = tNode.getParent();
while (parent != null && tNode == parent.getlChild()) {
tNode = parent;
parent = tNode.getParent();
}
return parent;
}
}
public void insert(int value) {
TreeNode tNode = new TreeNode(value);
insert(root, tNode);
}
private void insert(TreeNode xNode, TreeNode tNode)
{
TreeNode yNode = null;
while (xNode != null) {
yNode = xNode;
if (xNode.getKey() < tNode.getKey())
xNode = xNode.getrChild();
else
xNode = xNode.getlChild();
}
tNode.setParent(yNode);
if (yNode == null)
root = tNode;
else if (yNode.getKey() < tNode.getKey())
yNode.setrChild(tNode);
else {
yNode.setlChild(tNode);
}
}
private void transplant(TreeNode uNode, TreeNode vNode)
{
if (uNode == null)
root = vNode;
else {
if (uNode.getParent() == null)
root = vNode;
else if (uNode == uNode.getParent().getlChild())
uNode.getParent().setlChild(vNode);
else {
uNode.getParent().setrChild(vNode);
}
if (vNode != null)
vNode.setParent(uNode.getParent());
}
}
public boolean delete(int value)
{
if (!isContain(value))
return false;
else {
TreeNode tNode = search(root, value);
delete(tNode);
return true;
}
}
private void delete(TreeNode tNode)
{
if (tNode.getlChild() == null)
transplant(tNode, tNode.getrChild());
else if (tNode.getrChild() == null)
transplant(tNode, tNode.getlChild());
else {
TreeNode replaceNode = successor(tNode);
delete(replaceNode);
tNode.setKey(replaceNode.getKey());
}
}
/* private void delete(TreeNode tNode)
{
if (tNode.getlChild() == null)
transplant(tNode, tNode.getrChild());
else if (tNode.getrChild() == null)
transplant(tNode, tNode.getlChild());
else {
TreeNode replaceNode = successor(tNode);
if (replaceNode != tNode.getrChild()) {
transplant(replaceNode, replaceNode.getrChild());
//处理replaceNode的右孩子
replaceNode.setrChild(tNode.getrChild());
replaceNode.getrChild().setParent(replaceNode);
}
transplant(tNode, replaceNode);
//处理replaceNode的左孩子
replaceNode.setlChild(tNode.getlChild());
replaceNode.getlChild().setParent(replaceNode);
}
}*/
public boolean isEmpty()
{
return root == null ? true: false;
}
public void preorder()
{
preorder(root);
}
/* private void preorder(TreeNode tNode)
{
if (tNode != null) {
preorder(tNode.getlChild());
System.out.println(tNode);
preorder(tNode.getrChild());
}
}*/
private void preorder(TreeNode rootNode)
{
Stack<TreeNode> nodeStack = new Stack<>();
TreeNode tNode = rootNode;
while (tNode != null || !nodeStack.empty()) {
if (tNode == null) {
tNode = nodeStack.pop();
System.out.println(tNode);
tNode = tNode.getrChild();
} else {
nodeStack.push(tNode);
tNode = tNode.getlChild();
}
}
}
public static void main(String[] args)
{
BinarySearchTree bsTree = new BinarySearchTree();
System.out.println("bsTree isEmpty : " + bsTree.isEmpty());
bsTree.insert(3);
bsTree.insert(1);
bsTree.insert(2);
bsTree.insert(4);
bsTree.insert(0);
bsTree.insert(10);
bsTree.insert(6);
bsTree.insert(1);
bsTree.preorder();
System.out.println("bsTree isContain(3) : " + bsTree.isContain(3));
System.out.println("bsTree minValue is : " + bsTree.findMin());
System.out.println("-----------------------------------");
bsTree.delete(3);
System.out.println("hava deleted 3");
System.out.println("bsTree isContain(3) : " + bsTree.isContain(3));
bsTree.preorder();
System.out.println("bsTree isEmpty : " + bsTree.isEmpty());
}
}
</span>
运行结果: