搜索树数据结构支持许多动态集合操作,包括search、insert、delete、maximum、minimum等。因此,我们使用一棵搜索树既可以作为一个字典又可以作为一个优先队列。
二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。对于有n个结点的一棵完全二叉树来说,这些操作的最坏运行时间为O(logn)。然而,如果这棵树是一条n个结点组成的线性链,那么同样的操作就要花费O(n)的最坏的运行时间。
一、什么是二叉搜索树
一棵二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以使用一个链表数据结构来表示,其中每个结点就是一个对象。最重要的特性就是:对任何结点x,其左子树的关键字最大不超过x.value,其右子树中的关键字最小不低于x.value。不同二叉搜索树可以代表同一组值的集合,大部分搜索树的最坏运行时间与树的高度成正比。下图为一棵包含11个结点、高度为4的二叉搜索树。
二、二叉查找树的操作(查询、插入、遍历、删除)
定义结点
public class TreeNode{
public int keyValue; //关键字值
public TreeNode leftNode;//左节点
public TreeNode rightNode;//右节点
public TreeNode(){}
public TreeNode(int Key){
this.keyValue = Key;
}
}
(1)查询二叉搜索树的结点
//查询结点
public TreeNode BSTsearch(TreeNode root, int Key){
TreeNode node=root;
//当节点值不等于要查找的结点值就循环,如果没有找到则返回null
while(node.keyValue!=Key){
if(Key<node.keyValue){
node=node.leftNode;
}else{
node=node.rightNode;
}
if(node==null){
return null;
}
}
return node;
}
(2)插入二叉搜索树的结点
//插入结点,插入结点的过程就是先查询再插入
public void BSTinsert(TreeNode root, int Key){
TreeNode node=new TreeNode(Key);
//插入结点之前先找到要插入的位置,这样就要记住插入结点的父节点
//让父节点的左右指针指向要添加的结点
if(root== null){
root=node;
}else{
TreeNode currentNode = root;//定义当前节点为根节点
TreeNode parentNode;
while(true){
parentNode=currentNode;
if(Key<currentNode.keyValue){
currentNode=currentNode.leftNode;
if(currentNode==null){
parentNode.leftNode=node;
return;
}
}else{
currentNode=currentNode.rightNode;
if(currentNode==null){
parentNode.rightNode=node;
return;
}
}
}
}
}
(3)遍历二叉树的结点
//遍历树,主要分为中序遍历、前序遍历和后序遍历
//下面以中序遍历做例子
public void BSTdisplay(TreeNode node){
if(node!=null){
BSTdisplay(node.leftNode);
System.out.println(node.keyValue+",");
BSTdisplay(node.rightNode);
}
}
(4)查询二叉查找树中的最大结点
//最大值
public int BSTmax(TreeNode root){
TreeNode node=root;
TreeNode parent=null;
while(node!=null){
parent=node;
node=node.rightNode;
}
return parent.keyValue;
}
(5)查询二叉查找树中的最小结点
//最小值
public int BSTmin(TreeNode root){
TreeNode node=root;
TreeNode parent=null;
while(node!=null){
parent=node;
node=node.leftNode;
}
return parent.keyValue;
}
(6)删除二叉搜索树的结点
二叉树的结点删除要分成三类:
删除没有子结点的结点;
删除只有一个子结点的结点;
删除有两个子结点的结点。
// 删除节点分三种方式删除节点
// 1、删除没有子节点的节点,直接让该节点的父节点的左节点或右节点指向空
// 2、删除有一个子节点的节点,直接让该节点的父节点指向被删除节点的剩余节点
// 3、删除有三个节点的子节点,找到要删除节点的后继节点, 用该节点替代删除的节点
public boolean BSTdelete(TreeNode root,int Key) {
// 首先查找节点,并记录该节点的父节点引用
TreeNode current = root;
TreeNode parent = root;
boolean isLeftNode = true;
while (current.keyValue != Key) {
parent = current;
if (Key < current.keyValue) {
isLeftNode = true;
current = current.leftNode;
} else {
isLeftNode = false;
current = current.rightNode;
}
}
if (current == null) {
System.out.println("没有找到要删除的节点!");
return false;
}
// 下面分三种情况删除节点
if (current.leftNode == null && current.rightNode == null) { //要删除的节点没有子节点
if (current == root) { // 根节点就删除整棵树
root = null;
} else if (isLeftNode) { // 如果是左节点,做节点指向空
parent.leftNode = null;
} else { // 如果是右节点,右节点指向空
parent.rightNode = null;
}
} else if (current.leftNode == null) { //要删除的节点只有右节点
if (current == root) {
root = current.rightNode;
} else if (isLeftNode) {
parent.leftNode = current.rightNode;
} else {
parent.rightNode = current.rightNode;
}
} else if (current.rightNode == null) { //要删除的节点只有左节点
if (current == root) {
root = current.leftNode;
} else if (isLeftNode) {
parent.leftNode = current.leftNode;
} else {
parent.rightNode = current.leftNode;
}
} else { //要删除的节点有两个节点
TreeNode successor = findSuccessor(current);
if(current == root){
root = successor;
}else if(isLeftNode){
parent.leftNode = successor;
}else{
parent.rightNode = successor;
}
successor.leftNode = current.leftNode;
}
return true;
}
private TreeNode findSuccessor(TreeNode delNode){
TreeNode parent = delNode;
TreeNode successor = delNode;
TreeNode current = delNode.rightNode;
while(current != null){
parent = successor;
successor = current;
current = current.leftNode;
}
if(successor != delNode.rightNode){
parent.leftNode = successor.rightNode;
successor.rightNode = delNode.rightNode;
}
return successor;
}