二叉查找树介绍
二叉查找树作为一种最简单的二叉排序树,它是特殊的二叉树:对于二叉树,假设x为二叉树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y]>= key[x]。那么,这棵树就是二叉查找树。
二叉查找树具有如下性质:
1、如果节点左子树存在,那么左子树中的所有值均小于其根节点的值
2、如果节点右子树存在,那么右子树中所有的值均大于其根节点的值
3、任一节点的左右子树均为二叉搜索树
4、不存在键值相等的两个节点
二叉树结构定义
//定义二叉查找树节点的结构
class BSNode<T extends Comparable<T>> {
BSNode<T> parent;
BSNode<T> left;
BSNode<T> right;
T key;
BSNode(T key,BSNode<T> parent,BSNode<T> left,BSNode<T> right)
{
this.right = right;
this.left = left;
this.parent = parent;
this.key = key;
}
}
二叉查找树前序遍历(递归、非递归)
/**
* 采用递归方式对二叉查找树进行先序遍历,
* 先遍历根节点,再遍历左子树,最后遍历右子树
*/
public void preOrder()
{
preOrder(root);
}
public void preOrder(BSNode<T> node)
{
if(node!=null)
{
System.out.print(node.key);
preOrder(node.left);
preOrder(node.right);
}
}
非递归方式
public void iterPreOrder()
{
BSNode<T> node = root;
Stack<BSNode<T>> stack = new Stack<BSNode<T>>(); //栈用来存放前序遍历回退的路径
stack.push(null);//作为循环结束的条件
while(node!=null)
{
System.out.println(node.key); //输出节点
if(node.right!=null) //右子女入栈
{
stack.push(node.right);
}
if(node.left!=null){
node = node.left;
}else{
node = stack.pop(); //如果没有左子树,则从栈中回退一个节点
}
}
}
二叉查找树中序遍历(递归、非递归)
/**
* 采用递归方式对二叉查找树进行中序遍历
* 先遍历左子树,然后遍历根节点,最后遍历右子树
*/
public void inOrder()
{
inOrder(root);
}
private void inOrder(BSNode<T> node)
{
if(node!=null)
{
preOrder(node.left);
System.out.print(node.key);
preOrder(node.right);
}
}
非递归
/**
* 中序遍历思路
* 由于中序遍历先遍历左子树,然后遍历根节点,那么需要一个栈来记录遍历中需要回退的路径,
*
*/
public void iterInOrder()
{
Stack<BSNode<T>> stack = new Stack<BSNode<T>>(); //栈用来存放前序遍历回退的路径
BSNode<T> node = root;
do{
while(node!=null) //遍历左子树,并将根节点入栈
{
stack.push(node);
node = node.left;
}
if(!stack.isEmpty()) //如果栈非空,弹出根节点,输出,获取根节点的右子女
{
node = stack.pop();
System.out.print(node.key);
node = node.right;
}
}while(!stack.isEmpty()||node!=null);//当整个树的根节点的做子女遍历完毕时,栈为空,所以需要根据及诶单判断遍历是否进行
}
二叉查找树后序遍历(递归、非递归)
/**
* 采用递归方式对二叉查找树进行遍历,
* 先遍历左子树,在遍历右子树,最后遍历根节点
*/
public void postOrder()
{
postOrder(root);
}
private void postOrder(BSNode<T> node)
{
if(node!=null)
{
preOrder(node.left);
preOrder(node.right);
System.out.print(node.key);
}
}
非递归方式
//由于 需要记录栈中节点是在左子树还是在右子树,所以对BSNode进行封装,以记录其左右
class STKNode{
BSNode<T> node;
Tag tag; //采用枚举类型
STKNode(BSNode<T> node,Tag tag)
{
this.tag = tag;
this.node = node;
}
}
enum Tag{
L,R;
}
/**
* 采用非递归(迭代)的方式对二叉查找树进行后续遍历
*/
public void iterPostOrder()
{
Stack<STKNode> stack = new Stack<STKNode>();//记录需要回退的路径
BSNode<T> node = root;
do{
STKNode stk = null;
while(node!=null) //遍历左子树
{
stk = new STKNode(node,Tag.L);
stack.push(stk);
node = node.left;
}
boolean flag = true;
while(flag&&!stack.isEmpty())
{
STKNode stkNode = stack.pop();
switch(stkNode.tag){
case L: //遍历右子树
stkNode.tag = Tag.R;
stack.push(stkNode);
flag = false;
node = stkNode.node.right;
break;
case R:
System.out.println(stkNode.node.key);
break;
}
}
}while(!stack.isEmpty());
}
二叉查找树层序遍历
/**
* 对二叉查找树进行层序遍历
*/
public void levelOrder()
{
BSNode<T> node = null;
Queue<BSNode<T>> queue = new LinkedList<BSNode<T>>(); //用来存放每个节点的子女
queue.add(root); //将根节点加入队列
while(!queue.isEmpty())
{
node = queue.poll();
System.out.println(node.key);
if(node.left!=null) //如果左子女不为空将其接入队列
{
queue.add(node.left);
}
if(node.right!=null) //如果右子女不为空将其接入队列
{
queue.add(node.right);
}
}
}
二叉查找树插入
/**
* 向二叉查找树中插入一个节点
* @param key
*/
public void insert(T key)
{
if(key==null)
return;
if(search(key)!=null) //如果关键字已存在,返回
return;
BSNode<T> node = new BSNode<T>(key,null,null,null);
if(root ==null) //如果根节点为空,赋值给根节点
{
root = node;
return;
}
BSNode<T> temp = node; //临时节点
BSNode<T> pre = null;
int com=0;
while(temp!=null)
{
pre = temp;
com = temp.key.compareTo(key);
if(com>0)
temp = temp.left;
else
temp = temp.right;
}
if(com>0)
{
pre.left = node;
}else{
pre.right = node;
}
node.parent = pre;//指向父节点
}
二叉查找树查找
/**
* 从二叉查找树中查找一个关键字
* @param key
* @return
*/
public BSNode<T> search(T key)
{
if(key==null) //关键字不存在
return null;
BSNode<T> node = root;
int com; //记录比较的结果
while(node!=null)
{
com = node.key.compareTo(key);
if(com>0)
{
node = node.left;
}else if(com<0){
node = node.right;
}else{
return node; //返回
}
}
return null;
}
二叉查找树删除
/**
* 从二叉查找树中删除一个元素主要分三种情况:
* 当节点左右子女均为空时,直接删除该节点
* 当节点中只存在一个子女时,将待删除节点的位置直接修改为其子女
* 如果节点左右子女均存在时,找到其左子树中最大节点(前驱),或者右子树最小节点(后继)
* @param key
*/
public void remove(T key)
{
if(key==null)
return;
BSNode<T> curNode = search(key);//待删除节点
if(curNode==null) //如果关键字不存在,返回
return;
BSNode<T> left,right,parent;
left = curNode.left;
right = curNode.right;
parent = curNode.parent; //被删除节点的
BSNode<T> replace = null; //最终填充到被删除节点位置
int com = curNode.key.compareTo(parent.key);//确定该节点是左子女还是右子女
if(left==null&&right==null) //左右子女都不存在
{
parent = null;
}
if(left!=null&&right!=null) //左右子女都存在
{
BSNode<T> temp = curNode.right,pre = null;
while(temp!=null) //找到当前节点的左子树中最大节点(前驱)
{
pre = temp;
temp = curNode.right;
}
replace = pre;
if(pre.left!=null) //如果该节点有右子树,则将其右子树移动到该节点位置
{
pre.parent.right = pre.left;
pre.left.parent = pre.parent;
}
return;
}
if(left==null||left==null) //要删除节点只存在一个子女
{
BSNode<T> node = null;
if(left==null)//找到非空子女
{
node = right;
}else{
node = left;
}
replace = node;
}
if(com>0) //修改被解除节点的父亲的子女
{
parent.right = replace;
}else{
parent.left = replace;
}
replace.parent = parent;//将该节点父节点指向parent
replace.left = left;//设置其左右子女
replace.right = right;
}
二叉查找树销毁
/**
* 销毁 ,采用递归方式销毁左右子树
*/
public void destory()
{
destory(root);
}
private void destory(BSNode<T> node)
{
if(node!=null)
{
if(node.left!=null)
destory(node.left); //销毁左子树
if(node.right!=null)
destory(node.right);//销毁右子树
}
node = null;
}
本文参考如下博文:
http://www.cnblogs.com/skywang12345/p/3576452.html