博主最近在学习二叉树,在此记录下学习的相关历程和代码。
二叉搜索树
二叉搜索树的图像定义相信大家都已经很熟悉了,在此就不放图了,接下来直接给出二叉树定义的代码。
private static BSTNode{
String key;//存放的值
BSTNode left,right;//左子树分支,右子树分支
}
二叉搜索树中查找结点
那么既然上述二叉树的定义给出了,二叉搜索树的findNode方法也就自然不难给出了,该方法使用了二分搜索算法。
private static BSTNode findNode(BSTNode node,String key){
if(node==null){
return null;
}
int cmp=key.compareTo(node.key);
if(cmp==0){
return node;
}else if(cmp<0){
return findNode(node.left,key);
}else{
return findNode(node.right,key);
}
}
compareTo方法
在此解释一下刚才代码中所使用的String类型中的compareTo()方法,简单来说这个方法常见的用法是这样的
str1.compareTo(str2);
常用于比较str1和str2的字典顺序,该方法返回一个int值,如果值为0,那么两个字符串的字典顺序相同。如果为正数,则str1字典顺序在str2后。负数的话str1字典顺序在str2前。
在二叉搜索树中插入新结点
既然能在二叉搜索树中查找到相应的结点,那么新的问题也就随之而来:这些结点是如何被插入进去的?简单来讲就是调用insertNode方法从树的根开始递归处理。在每个结点中insertNode必须将新键与当前结点中的键比较。如果新键在现有键之前则新键属于其左子树,反之新键在现有键之后则属于其右子树。然后再递归过程中会碰到一颗空子树,新节点应该添加到这个位置。
简单来说就是一下三步递归调用:
1.结点为空,直接添加。
2.该结点值的字典顺序在要添加的值之后,新结点添加到该结点左子树。
3.该结点值的字典顺序在要添加的值之前,新结点添加到该结点右子树。
思路有了,那么下面来编写insertNode方法代码。
private static BSTNode insertNode(BSTNode node,String key){
if(node==null){
node=new BSTNode();
node.key=key;
node.left=node.right=null;
}else{
int cmp=key.compareTo(node.key);
if(cmp<0){
node.left=insertNode(node.left,String key);
} else if(cmp>0){
node.right=insertNode(node.right,String key);
}
}
return node;
}
二叉搜索树中移除结点
首先要了解的是移除是一个复杂的过程,该过程不仅包括查找更包括如何去移除。添加这个过程是容易的,因为将结点插入并不会对二叉搜索树的结构有什么致命性的伤害,而移除则不一样。我们要考虑的有以下三种状态:
1.移除的结点没有孩子。
2.移除的结点只有左孩子或右孩子中的一个。
3.移除的结点既有左孩子又有右孩子。
针对上述三种情况我给出的解决方法如下:
1.直接移除(指向该节点的引用替换为null引用)。
2.将该结点的孩子替换到这个结点的位置。
3.将该结点左孩子的右孩子替换这个结点的位置。
该解决方法在保证了移除结点的前提下还保证了二叉搜索树的排序关系。
那么既然思路有了,那么思路中的一个关键性问题就要解决,那就是如何做到“指向该节点的引用替换为null引用”。那么要解决的事情就只有一个了,那就是求出这个结点的父节点,下面直接贴出代码:
private static BSTNode getParentNode(BSTNode node,String key){
if(node==null||node.left.key==key||node.right.key==key){
return node;
}
BSTNode left=getParentNode(node.left,key);
if(!left=null){
return left;
}
BSTNode right=getParentNode(node.right,key);
if(!right=null){
return right;
}
return left;//这里return left还是right都无所谓,因为返回的值都是null
}
那么现在就可以按照上述思路来给出代码了:
public static BSTNode removeNode(BSTNode node,String key){
if(findNode(node, key).left==null&&findNode(node, key).right==null) {
if(getParentNode(node,key).left.left.key==key) {
getParentNode(node,key).left=null;
}else {
getParentNode(node,key).right=null;
}
}else if(findNode(node, key).left!=null&&findNode(node, key).right!=null){
String cmp=findNode(node, key).left.right.key;
removeNode(node,cmp);
findNode(node, key).key=cmp;
}else {
if(findNode(node, key).left!=null) {
String cmp=findNode(node, key).left.key;
removeNode(node,cmp);
findNode(node, key).key=cmp;
}else {
String cmp=findNode(node, key).right.key;
removeNode(node,cmp);
findNode(node, key).key=cmp;
}
}
return node;
}
树的遍历
前序、中序以及后序遍历这些老生常谈的东西我就不多赘述了,下面直接贴上代码。
前序遍历:
void displaytree(BSTNode node){
if(node!=null){
System.out.println(node.key);
displaytree(node.left);
displaytree(node.right);
}
}
中序遍历:
void displaytree(BSTNode node){
if(node!=null){
displaytree(node.left);
System.out.println(node.key);
displaytree(node.right);
}
}
后序遍历:
void displaytree(BSTNode node){
if(node!=null){
displaytree(node.left);
displaytree(node.right);
System.out.println(node.key);
}
}