二叉排序树删除节点的思路
情况1:删除的当前节点无左右孩子节点,那么就直接将当前要删除的节点设置为null即可。
情况2:删除的当前节点无左孩子节点,有右孩子节点,那么就将当前要删除的节点设置为右孩子节点即可。
情况3:删除的当前节点无右孩子节点,有左孩子节点,那么就将当前要删除的节点设置为左孩子节点即可。
情况4:删除的当前节点有右孩子节点也有左孩子节点,那么就选出右孩子树中最小的点,设置为当前要删除的节点即可。这种方式既可以保证二叉排序树的性质,由于右孩子树中最小的点,无左孩子节点(如果有左孩子节点,那么就不符合二叉树性质了)。
二叉排序树删除节点的实现
由于网上的大多博客和书上的例子大多都是采用递归的方式来实现的,我的性格总想写点自己的东西,采用了非递归的方式实现的。
二叉排序树结构
class BSTNode{
private Integer data=;
private BSTNode lchild=;
private BSTNode rchild=;
public BSTNode() {}
public BSTNode(Integer data) {
this.data = data;
}
public Integer getData() {
return data;
}
public void setData(Integer data) {
this.data = data;
}
public BSTNode getLchild() {
return lchild;
}
public void setLchild(BSTNode lchild) {
this.lchild = lchild;
}
public BSTNode getRchild() {
return rchild;
}
public void setRchild(BSTNode rchild) {
this.rchild = rchild;
}
}
返回值结构
定义了这个结构为了便于方法的返回值,结构的思想是存储当前的节点,和其对应的父节点。
class RetNode{
private BSTNode bstNodeKey;
private BSTNode parentNode;
public RetNode(){}
public RetNode(BSTNode bstNodeKey, BSTNode parentNode) {
super();
this.bstNodeKey = bstNodeKey;
this.parentNode = parentNode;
}
public BSTNode getBstNodeKey() {
return bstNodeKey;
}
public void setBstNodeKey(BSTNode bstNodeKey) {
this.bstNodeKey = bstNodeKey;
}
public BSTNode getParentNode() {
return parentNode;
}
public void setParentNode(BSTNode parentNode) {
this.parentNode = parentNode;
}
}
构造的二叉排序树图:
情况4中删除节点30后的结果图:
步骤1:查询要删除的节点的位置
//采用非递归的结构来查找要删除的节点,同时也为了方便查找删除节点的父节点
private static RetNode queryBSTNode(BSTNode bstNode,Integer key){
Stack<BSTNode> parentStack =new Stack<BSTNode>();
while(bstNode !=null){
if(bstNode.getData().equals(key)){
if(parentStack.isEmpty()){
return new RetNode(bstNode,null);
}else{
return new RetNode(bstNode,parentStack.pop());
}
}else if(bstNode.getData()>key){
parentStack.push(bstNode);
bstNode =bstNode.getLchild();
}else{
parentStack.push(bstNode);
bstNode =bstNode.getRchild();
}
}
return null;
}
就是采用二叉排序树的非递归遍历结构实现的,里面采用了辅助的栈结构,为了方便的可以获取到当前节点的父节点。回溯法的思想不多说了。
步骤2:对于前面的思路中的情况4中获取右子树中的值最小的节点,并调整节点的结构的实现
//获取右子树中的最小值
private static BSTNode getRchildMin(BSTNode bstNode){
Stack<BSTNode> minParentStack =new Stack<BSTNode>();
BSTNode min =,temp =bstNode.getRchild();
minParentStack.push(temp);
BSTNode insertNode =temp;
while(temp !=null){
if(temp.getLchild() !=null){
minParentStack.push(temp);
min=temp;
temp =temp.getLchild();
}else if(temp.getLchild() ==null && temp.getRchild() !=null){
min=temp;
BSTNode minParent =minParentStack.pop();
minParent.setLchild(null);
while(temp !=null){
if(temp.getRchild() ==null){
temp.setRchild(insertNode);
break;
}else{
temp=temp.getRchild();
}
}
break;
}else{
min=temp;
break;
}
}
return min;
}
这部分也是最烦死我的地方,我结合前面 “构造二叉排序图” 说:采用辅助栈,是为了获取删除节点30的右子树中最小值节点33的父节点35,我们将最小节点33的父节点35等其他长辈节点(就是33同层或者上层的点)都给它设置到最小值节点33的右子树中叶子节点34的右子树。所以要清楚的获取几个点,删除节点的右子树的最高点50,删除节点的右子树中的值最小的节点33,删除节点的右子树中的值最小的节点的父节点35,删除节点的右子树中的值最小的节点的右子树中最右边的叶子节点34。
步骤3:对于思路中的情况1到情况4的实现
public static BSTNode deleteBSTNode(BSTNode bstNode,Integer key){
RetNode retNode =queryBSTNode(bstNode,key);
if(retNode ==null){
System.out.println("您打算删除的值不存在");
return null;
}
//要删除的节点
BSTNode bstNodeKey =retNode.getBstNodeKey();
//求出要删除节点的父节点
BSTNode parentNode =retNode.getParentNode();
//1代表左孩子节点,0代表右孩子节点
String pos ="";
if(parentNode !=null){
if(parentNode.getLchild() !=null && parentNode.getLchild().getData().equals(bstNodeKey.getData())){
pos="1";
}else{
pos="0";
}
}
if(bstNodeKey ==null){
System.out.println("您打算删除的值不存在");
return null;
}else{
if(bstNodeKey.getRchild() ==null){
if(bstNodeKey.getLchild() ==null){
bstNodeKey =;//左右子树都为空
}else{
bstNodeKey =bstNodeKey.getLchild();//左子树不为空,右子树为空
}
}else{
if(bstNodeKey.getLchild() ==null){
bstNodeKey =bstNodeKey.getRchild();//右子树不为空,左子树为空
}else{
BSTNode minNode =getRchildMin(bstNodeKey);//左右子树都不为空
minNode.setLchild(bstNodeKey.getLchild());
bstNodeKey=minNode;
}
}
}
if(pos.equals("1")){
parentNode.setLchild(bstNodeKey);
}else if(pos.equals("0")){
parentNode.setRchild(bstNodeKey);
}else{
bstNode =bstNodeKey;
}
return bstNode;
}
步骤4.测试程序
构造二叉排序树:
/*构造二叉排序树
* */
public static Integer sortBSTNode(BSTNode bstNode,BSTNode root){
if(bstNode ==null){
bstNode =new BSTNode();
bstNode.setLchild(null);
bstNode.setRchild(null);
return 9999;
}else if(root.getData()>bstNode.getData()){
if(root.getLchild()==null){
root.setLchild(bstNode);
}else{
return sortBSTNode(bstNode,root.getLchild());
}
}else{
if(root.getRchild() ==null){
root.setRchild(bstNode);
}else{
return sortBSTNode(bstNode,root.getRchild());
}
}
return 9999;
}
二叉排序树前序遍历(和二叉树的前序遍历思路一样)
/*递归方式遍历二叉排序树
* 前序方式
* */
public static void queryBSTNodeByPre(BSTNode bstNode){
if(bstNode !=null){
System.out.print(bstNode.getData()+" - ");
queryBSTNodeByPre(bstNode.getLchild());
queryBSTNodeByPre(bstNode.getRchild());
}else{
return;
}
}
二叉排序树中序遍历(和二叉树的中序遍历思路一样)
/*递归方式遍历二叉排序树
* bstNod:为遍历二叉排序树的开始节点
* 中序方式
* */
public static void queryBSTNodeByOrder(BSTNode bstNode) {
if(bstNode !=null){
queryBSTNodeByOrder(bstNode.getLchild());
System.out.print(bstNode.getData()+" - ");
queryBSTNodeByOrder(bstNode.getRchild());
}else{
return;
}
}
测试
public static void main(String[] args) {
Integer[] arr={100,30,200,20,50,10,25,40,60,55,70,35,45,33,37,34};
List<BSTNode> list =new ArrayList<BSTNode>();
for(int i=0;i<arr.length;i++){
list.add(new BSTNode(arr[i]));
}
BSTNode root =list.get(0);
for(int i=1;i<list.size();i++){
sortBSTNode(list.get(i),root);
}
deleteBSTNode(root,30);
System.out.println("前序遍历:");
queryBSTNodeByPre(root);
System.out.println();
System.out.println("中序遍历:");
queryBSTNodeByOrder(root);
}
至于情况1到情况3的测试就不在这里写了(我都测试过了),只对最难的情况4进行了测试。花了两天午休的时间和一天下午的休息时间终于把这个非递归的二叉排序树删除写好了。最近写树,虽然累点,但是感觉把如何使用栈实现回溯法和递归练得熟练点了,调整思想准备编写下一个程序,将二叉排序树进行平衡化。