二叉查找树(二叉排序树)的链式实现


在二叉树里,提到过,并没有去实现插入(只有在指定位置插入),删除操作,因为对于树形结构,跟线性结构不一样,插入没有固定的位置,删除会导致树的形状发生改变。

二叉排序树是在二叉树的基础上,对结点元素增添了有序(Comparable)特性。递归的有:  左子节点   <  根节点  <  右子节点,这样插入就有了唯一的位置,删除导致树形结构的破坏也有规则来重新调整树的结构。


1,二叉查找树的接口ADT

一般来说,二叉排序树要实现下列操作:

ContractedBlock.gif ExpandedBlockStart.gif BinarySearchTreeADT
 
   
package Tree;

public interface BinarySearchTreeADT extends BinaryTreeADT {

public void addElement(Comparable element);

public Comparable removeElement(Comparable target);

public Comparable findMin();

public Comparable findMax();

public Comparable removeMin();

public Comparable removeMax();

}



2,存储方式和构造方法


继承二叉树,为了便于实现,在二叉查找树里的结点类型里面,除left,right外再添加一个parent指针。

 
  
// private int count;
// private BinaryTreeNode root;


// 只用到了两个构造函数,因为在二叉查找树里我们已经定义了顺序属性的addElement操作

public BinarySearchTree()
{
super ();
}

public BinarySearchTree(Comparable element)
{
super (element);
}


继承部分参见上一篇二叉树内的实例变量和构造函数。


3,主要方法实现的分析


1)public void addElement(Comparable element)


根据插入元素的大小,从上往下,如果比当前节点小,往left走,如果大,往right走,知道element比某个结点大且这个节点的右子节点为空或者element比某个结点小,且这个结点的左子节点为空,插入即可

 
  
public void addElement(Comparable element) { // 添加节点

BinaryTreeNode temp
= new BinaryTreeNode(element);

if (root == null )
{
root
= temp;
root.parent
= null ;
}
else
{
BinaryTreeNode current
= root; // 用current来找插入到的那个结点
boolean added = false ;

while ( ! added)
{
if (element.compareTo(current.element) < 0 ) // 比当前节点小
if (current.left == null )
{
current.left
= temp;
current.left.parent
= current;
added
= true ;
}
else
current
= current.left;
else // 比当前节点大
if (current.right == null )
{
current.right
= temp;
current.right.parent
= current;
added
= true ;
}
else
current
= current.right;
}
}
count
++ ;
}




2)public Comparable removeElement(Comparable target)

删除一个结点A要分3种情况

a,  A既没有左子树也没有右子树,直接将A节点处置空即可,注意置空的方法:A.parent.left = null  或者  A.parent.right = null;

b,  A有左子树没有右子树,或者相反,把左子树/右子树衔接到被删节点处即可

c  A既有左子树也有右子树,那么需要在A的左子树上找到A的中序直接前驱(即左子树上的最右结点){或者在A的右子树上找到A的直接后继,即右子树上的最左节点,我实现的是找直接前驱},用直接前驱替代A,这时候直接前驱可能还有左子树(绝对没有右子树,它是最右结点),所以还要讨论有没有左子树的情况,有左子树的话,需要把左子树再衔接到直接前驱的位置。

另外实现上还有一些细节,下面是删除的实现:

 
  
public Comparable removeElement(Comparable target) { // 删除节点

Comparable result
= null ;
if ( ! isEmpty())
{
if (root.element.equals(target)) // 如果要删的是根节点
{
result
= (Comparable) root.element;
if (root.left == null && root.right == null )
root
= null ;
else if (root.left != null && root.right == null )
root
= root.left; // 注意这里可以直接引用赋值,因为没有右子树,root就是指向左节点了
else if (root.right != null && root.left == null )
root
= root.right;
else
{
BinaryTreeNode temp
= root.left; // 找直接前驱
while (temp.right != null )
temp
= temp.right;

root.element
= temp.element; // 直接前驱赋值到root
// root = temp;错 引用赋值,事实上这样root就指向了原来temp的位置

if (temp.left == null )
{
if (temp.parent != root)
temp.parent.right
= null ;
else root.left = null ;
}
else {
temp.element
= temp.left.element;
temp.left
= null ;
}
}
count
-- ;
}
else // 否则不是根节点,要先找到删除的节点
{
BinaryTreeNode current
= root; // current来找删除的节点
boolean found = false ;

if (target.compareTo(root.element) < 0 )
current
= root.left;
else current = root.right;

while (current != null && ! found)
{
if (current.element.equals(target)) // 找到了要删的current
{
result
= (Comparable) current.element;
found
= true ; // 如果找到了就改变while循环的found条件,退出循环
count -- ;

// 删除节点后的调整!!!
if (current == current.parent.left)
{
if (current.left == null && current.right == null )
current.parent.left
= null ;
else if (current.left != null && current.right == null )
current.parent.left
= current.left;
else if (current.right != null && current.left == null )
current.parent.left
= current.right;
else
{
BinaryTreeNode temp
= current.left; // 找直接前驱
while (temp.right != null )
temp
= temp.right;
current.element
= temp.element;

if (temp.left == null )
{
if (temp.parent != current)
temp.parent.right
= null ;
else current.left = null ;
}
else {
temp.element
= temp.left.element;
temp.left
= null ;
}
}
}
else
{
if (current.left == null && current.right == null )
current.parent.right
= null ;
else if (current.left != null && current.right == null )
current.parent.right
= current.left;
else if (current.right != null && current.left == null )
current.parent.right
= current.right;
else
{
BinaryTreeNode temp
= current.left; // 找直接前驱
while (temp.right != null )
temp
= temp.right;
current.element
= temp.element;

if (temp.left == null )
{
if (temp.parent != current)
temp.parent.right
= null ;
else current.left = null ;
}
else {
temp.element
= temp.left.element;
temp.left
= null ;
}
}
}
}
else // 否则如果没有找到的话,继续往下找,在这改变while循环的current条件,继续循环
{
if (target.compareTo(current.element) < 0 )
current
= current.left;
else current = current.right;
}
}
if ( ! found)
{
System.out.println(
" 没有找到要删除的元素 " );
return null ;
}
}
}
return result;
}



这个实现的有点混乱,实际上把查找删除元素target和删除它写在了一个方法里,显得很臃肿,而且重复代码很多,复用性不好,不过就这样吧,也不想重写一个了,好麻烦




3)public Comparable removeMin()

实现起来思想不难,就是找到最左结点,但最左结点可能还有一个右子树,所以删除后要把右子树衔接起来,这里说一个语法问题,代码里面注释也写到了:


在删除二叉排序树上的最小值的时候,一直往左下搜寻,找到最小值的结点temp(temp肯定没有左子树了,往左到了尽头),这时temp可能有两种情况:1,没有右子树,没有右子树的时候删除temp,应该是temp.parent.left = null;而不能temp = null;.  2,还有一个右子树,应该是temp.parent.left = temp.right;   而不能是temp = temp.right;

这是因为temp在代码里写的是一个局部变量,在方法调用后就销毁了,对temp的操作,对树没有任何影响,但是可以在temp存在期间通过局部的temp来对它的父节点操作

 
  
public Comparable removeMin() {
if (isEmpty())
{
System.out.println(
" 树为空!!! " );
return null ;
}

BinaryTreeNode temp
= root;
while (temp.left != null )
temp
= temp.left;
Comparable result
= (Comparable) temp.element;

if (temp == root)
root
= null ;
else
{
if (temp.right == null )
temp.parent.left
= null ;
// temp = null; // 错误 同下
else
temp.parent.left
= temp.right; // 不是赋值,是要把temp的右子树和temp的parent连起来
// temp = temp.right; // 错误 temp是个局部变量!!!!
}
count
-- ;
return result;
}




4,完整清单和测试

ContractedBlock.gif ExpandedBlockStart.gif LinkedBinarySearchTree
 
   
package Tree;

import java.util.Iterator;

// 继承自二叉树,增添了parent指针,维护一个节点的父节点

public class BinarySearchTree extends BinaryTree implements BinarySearchTreeADT{

// private int count;
// private BinaryTreeNode root;


// 只用到了两个构造函数,因为在二叉查找树里我们已经定义了顺序属性的addElement操作

public BinarySearchTree()
{
super ();
}

public BinarySearchTree(Comparable element)
{
super (element);
}


// 扩展的操作

public void addElement(Comparable element) { // 添加节点

BinaryTreeNode temp
= new BinaryTreeNode(element);

if (root == null )
{
root
= temp;
root.parent
= null ;
}
else
{
BinaryTreeNode current
= root; // 用current来找插入到的那个结点
boolean added = false ;

while ( ! added)
{
if (element.compareTo(current.element) < 0 ) // 比当前节点小
if (current.left == null )
{
current.left
= temp;
current.left.parent
= current;
added
= true ;
}
else
current
= current.left;
else // 比当前节点大
if (current.right == null )
{
current.right
= temp;
current.right.parent
= current;
added
= true ;
}
else
current
= current.right;
}
}
count
++ ;
}


public Comparable removeElement(Comparable target) { // 删除节点

Comparable result
= null ;
if ( ! isEmpty())
{
if (root.element.equals(target)) // 如果要删的是根节点
{
result
= (Comparable) root.element;
if (root.left == null && root.right == null )
root
= null ;
else if (root.left != null && root.right == null )
root
= root.left; // 注意这里可以直接引用赋值,因为没有右子树,root就是指向左节点了
else if (root.right != null && root.left == null )
root
= root.right;
else
{
BinaryTreeNode temp
= root.left; // 找直接前驱
while (temp.right != null )
temp
= temp.right;

root.element
= temp.element; // 直接前驱赋值到root
// root = temp;错 引用赋值,事实上这样root就指向了原来temp的位置

if (temp.left == null )
{
if (temp.parent != root)
temp.parent.right
= null ;
else root.left = null ;
}
else {
temp.element
= temp.left.element;
temp.left
= null ;
}
}
count
-- ;
}
else // 否则不是根节点,要先找到删除的节点
{
BinaryTreeNode current
= root; // current来找删除的节点
boolean found = false ;

if (target.compareTo(root.element) < 0 )
current
= root.left;
else current = root.right;

while (current != null && ! found)
{
if (current.element.equals(target)) // 找到了要删的current
{
result
= (Comparable) current.element;
found
= true ; // 如果找到了就改变while循环的found条件,退出循环
count -- ;

// 删除节点后的调整!!!
if (current == current.parent.left)
{
if (current.left == null && current.right == null )
current.parent.left
= null ;
else if (current.left != null && current.right == null )
current.parent.left
= current.left;
else if (current.right != null && current.left == null )
current.parent.left
= current.right;
else
{
BinaryTreeNode temp
= current.left; // 找直接前驱
while (temp.right != null )
temp
= temp.right;
current.element
= temp.element;

if (temp.left == null )
{
if (temp.parent != current)
temp.parent.right
= null ;
else current.left = null ;
}
else {
temp.element
= temp.left.element;
temp.left
= null ;
}
}
}
else
{
if (current.left == null && current.right == null )
current.parent.right
= null ;
else if (current.left != null && current.right == null )
current.parent.right
= current.left;
else if (current.right != null && current.left == null )
current.parent.right
= current.right;
else
{
BinaryTreeNode temp
= current.left; // 找直接前驱
while (temp.right != null )
temp
= temp.right;
current.element
= temp.element;

if (temp.left == null )
{
if (temp.parent != current)
temp.parent.right
= null ;
else current.left = null ;
}
else {
temp.element
= temp.left.element;
temp.left
= null ;
}
}
}
}
else // 否则如果没有找到的话,继续往下找,在这改变while循环的current条件,继续循环
{
if (target.compareTo(current.element) < 0 )
current
= current.left;
else current = current.right;
}
}
if ( ! found)
{
System.out.println(
" 没有找到要删除的元素 " );
return null ;
}
}
}
return result;
}


public Comparable findMax() {
if (isEmpty())
{
System.out.println(
" 树为空!!! " );
return null ;
}
BinaryTreeNode temp
= root;
while (temp.right != null )
temp
= temp.right;
return (Comparable) temp.element;
}

public Comparable findMin() {
if (isEmpty())
{
System.out.println(
" 树为空!!! " );
return null ;
}
BinaryTreeNode temp
= root;
while (temp.left != null )
temp
= temp.left;
return (Comparable) temp.element;
}


public Comparable removeMax() {
if (isEmpty())
{
System.out.println(
" 树为空!!! " );
return null ;
}

BinaryTreeNode temp
= root;
while (temp.right != null )
temp
= temp.right;
Comparable result
= (Comparable) temp.element;

if (temp == root)
root
= null ;
else
{
if (temp.left == null )
temp.parent.right
= null ;
else
temp.parent.right
= temp.left;
// {
// temp.element = temp.left.element;
// temp.left = null;
// }
}
count
-- ;
return result;
}

public Comparable removeMin() {
if (isEmpty())
{
System.out.println(
" 树为空!!! " );
return null ;
}

BinaryTreeNode temp
= root;
while (temp.left != null )
temp
= temp.left;
Comparable result
= (Comparable) temp.element;

if (temp == root)
root
= null ;
else
{
if (temp.right == null )
temp.parent.left
= null ;
// temp = null; // 错误 同下
else
temp.parent.left
= temp.right; // 不是赋值,是要把temp的右子树和temp的parent连起来
// temp = temp.right; // 错误 temp是个局部变量!!!!
}
count
-- ;
return result;
}


public static void main(String[] args) {

BinarySearchTree tree
= new BinarySearchTree();

// 二叉排序树的形状跟插入顺序有关,中序序列总是不变(有序)
tree.addElement( 10 );
tree.addElement(
5 );
tree.addElement(
3 );
tree.addElement(
7 );
tree.addElement(
6 );
tree.addElement(
9 );
tree.addElement(
8 );
tree.addElement(
13 );
tree.addElement(
11 );
tree.addElement(
20 );
tree.addElement(
25 );
tree.addElement(
16 );

System.out.println(
" \n中序遍历结果为: " );
Iterator it
= tree.iteratorInorder();
while (it.hasNext())
System.out.print(it.next()
+ " " );

System.out.println(
" \n前序遍历结果为: " );
it
= tree.PreInorder();
while (it.hasNext())
System.out.print(it.next()
+ " " );

System.out.println(
" \n后序遍历结果为: " );
it
= tree.PostInorder();
while (it.hasNext())
System.out.print(it.next()
+ " " );
System.out.println(
" \n\n " + " 最小元素为: " + tree.findMin());
System.out.println(
" \n " + " 最大元素为: " + tree.findMax());

tree.removeMin();

System.out.println(
" \n删除最小元素3后的前序序列: " );
it
= tree.PreInorder();
while (it.hasNext())
System.out.print(it.next()
+ " " );


tree.removeMin();

System.out.println(
" \n\n接着删除最小元素5后的前序序列: " );
it
= tree.PreInorder();
while (it.hasNext())
System.out.print(it.next()
+ " " );

// tree.removeElement(10);
// tree.removeElement(9);
// tree.removeElement(13);
// tree.removeElement(10);
// tree.removeElement(5);
// System.out.println("\n\n删除节点后前序遍历结果为: ");
// it = tree.PreInorder();
// while(it.hasNext())
// System.out.print(it.next() + " ");
}

}


在main函数里构造了如下二叉排序树:

2011051401033261.jpg


旧金山大学计算机系弄了一个在线的可视化数据结构模拟,前几天google推荐给我的,感觉非常好,国内咋就没有这么好的东西呢,这个图是在那生成的,省去了我许多画图的麻烦,http://www.cs.usfca.edu/~galles/visualization/Algorithms.html



测试结果:

中序遍历结果为:
3  5  6  7  8  9  10  11  13  16  20  25 
前序遍历结果为:
10  5  3  7  6  9  8  13  11  20  16  25 
后序遍历结果为:
3  6  8  9  7  5  11  16  25  20  13  10 

最小元素为: 3

最大元素为: 25

删除最小元素3后的前序序列:
10  5  7  6  9  8  13  11  20  16  25 

接着删除最小元素5后的前序序列:
10  7  6  9  8  13  11  20  16  25

转载于:https://www.cnblogs.com/jmzz/archive/2011/05/14/2045994.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值