二叉查找树(binary search tree)
1)概念:对于树中的每个节点n,其左子节点中保存的所有数值都小于n保存的数值,右子节点保存的数值都大于n保存的数值。
2)二叉查找树可以实现更为优越的查找性能,主要实现方式有数组和链表结构,相比较而言,链表实现更为容易,因为数组实现删除和添加功能需要移动数组元素(如填补删除空位等)
今天下午在打印问题搞定后用C#实现了一下,比java版本比较有趣的使用C#的delegate来代替遍历二叉树时的visit方法,这样一来可以在遍历时对节点进行你所想要的任何操作。我们知道C#的delegate是类型化的函数指针,而C++的函数指针可以模仿动态语言的闭包或者匿名函数。这里也有这样的味道。
代码如下,只实现了整数型的,节点定义:
public
class
BSTIntNode
{
public int value;
public BSTIntNode left;
public BSTIntNode right;
public BSTIntNode( int value, BSTIntNode left, BSTIntNode right)
{
this .value = value;
this .left = left;
this .right = right;
}
public BSTIntNode( int value)
{
this .value = value;
this .left = null ;
this .right = null ;
}
}
{
public int value;
public BSTIntNode left;
public BSTIntNode right;
public BSTIntNode( int value, BSTIntNode left, BSTIntNode right)
{
this .value = value;
this .left = left;
this .right = right;
}
public BSTIntNode( int value)
{
this .value = value;
this .left = null ;
this .right = null ;
}
}
然后定义一个Delegate,作为遍历时的访问方法:
public
delegate
void
Visit(BSTIntNode node);
然后就是二叉树的实现,删除算法只实现了复制删除法:
public
class
BSTIntTree
{
protected BSTIntNode root;
public Visit visit;
public BSTIntTree()
{
this .root = null ;
}
private BSTIntNode Search(BSTIntNode node, int el)
{
while (node != null )
{
if (el == node.value)
return node;
else if (el < node.value)
node = node.left;
else
node = node.right;
}
return null ;
}
// 查找
public BSTIntNode Search( int el)
{
return Search(root, el);
}
// 广度优先遍历,利用队列实现,至上而下,至左而右
public void BreadthFirst()
{
BSTIntNode p = root;
Queue queue = new ListQueue();
if (p != null )
{
queue.Enqueue(p);
while ( ! queue.IsEmpty())
{
p = (BSTIntNode)queue.Dequeue();
visit(p);
if (p.left != null )
queue.Enqueue(p.left);
if (p.right != null )
queue.Enqueue(p.right);
}
}
}
// 深度优先遍历,递归实现线序,中序和后序
// 先序
protected void PreOrder(BSTIntNode p)
{
if (p != null )
{
visit(p);
PreOrder(p.left);
PreOrder(p.right);
}
}
public void PreOrder()
{
PreOrder(root);
}
// 中序
protected void InOrder(BSTIntNode p)
{
if (p != null )
{
InOrder(p.left);
visit(p);
InOrder(p.right);
}
}
public void InOrder()
{
InOrder(root);
}
// 后序
protected void PostOrder(BSTIntNode p)
{
if (p != null )
{
PostOrder(p.left);
PostOrder(p.right);
visit(p);
}
}
public void PostOrder()
{
PostOrder(root);
}
// 插入节点操作
public void Insert( int el)
{
BSTIntNode p = root, prev = null ;
// 查找节点位置
while (p != null )
{
prev = p;
if (p.value < el)
p = p.right;
else
p = p.left;
}
if (root == null ) // 空树
root = new BSTIntNode(el);
else if (prev.value < el) // 大于节点,插入右子树
prev.right = new BSTIntNode(el);
else
prev.left = new BSTIntNode(el);
}
// 复制删除法的实现,归并删除法可能改变树的高度
public void Delete( int el)
{
BSTIntNode node, p = root, prev = null ;
// 查找节点位置
while (p != null && p.value != el)
{
prev = p;
if (p.value < el)
p = p.right;
else
p = p.left;
}
node = p;
if (p != null && p.value == el)
{
if (node.right == null )
node = node.left;
else if (node.left == null )
node = node.right;
else
{
BSTIntNode temp = node.left;
BSTIntNode previous = node;
while (temp.right != null ) // 查找左字节数的最右子节点
{
previous = temp;
temp = temp.right;
}
node.value = temp.value;
if (previous == node)
previous.left = temp.left;
else
previous.right = temp.left;
}
if (p == root)
root = node;
else if (prev.left == p)
prev.left = node;
else
prev.right = node;
}
else if (root != null )
{
Console.WriteLine( " 没有找到节点:{0} " , el);
}
else
Console.WriteLine( " 树为空! " );
}
}
{
protected BSTIntNode root;
public Visit visit;
public BSTIntTree()
{
this .root = null ;
}
private BSTIntNode Search(BSTIntNode node, int el)
{
while (node != null )
{
if (el == node.value)
return node;
else if (el < node.value)
node = node.left;
else
node = node.right;
}
return null ;
}
// 查找
public BSTIntNode Search( int el)
{
return Search(root, el);
}
// 广度优先遍历,利用队列实现,至上而下,至左而右
public void BreadthFirst()
{
BSTIntNode p = root;
Queue queue = new ListQueue();
if (p != null )
{
queue.Enqueue(p);
while ( ! queue.IsEmpty())
{
p = (BSTIntNode)queue.Dequeue();
visit(p);
if (p.left != null )
queue.Enqueue(p.left);
if (p.right != null )
queue.Enqueue(p.right);
}
}
}
// 深度优先遍历,递归实现线序,中序和后序
// 先序
protected void PreOrder(BSTIntNode p)
{
if (p != null )
{
visit(p);
PreOrder(p.left);
PreOrder(p.right);
}
}
public void PreOrder()
{
PreOrder(root);
}
// 中序
protected void InOrder(BSTIntNode p)
{
if (p != null )
{
InOrder(p.left);
visit(p);
InOrder(p.right);
}
}
public void InOrder()
{
InOrder(root);
}
// 后序
protected void PostOrder(BSTIntNode p)
{
if (p != null )
{
PostOrder(p.left);
PostOrder(p.right);
visit(p);
}
}
public void PostOrder()
{
PostOrder(root);
}
// 插入节点操作
public void Insert( int el)
{
BSTIntNode p = root, prev = null ;
// 查找节点位置
while (p != null )
{
prev = p;
if (p.value < el)
p = p.right;
else
p = p.left;
}
if (root == null ) // 空树
root = new BSTIntNode(el);
else if (prev.value < el) // 大于节点,插入右子树
prev.right = new BSTIntNode(el);
else
prev.left = new BSTIntNode(el);
}
// 复制删除法的实现,归并删除法可能改变树的高度
public void Delete( int el)
{
BSTIntNode node, p = root, prev = null ;
// 查找节点位置
while (p != null && p.value != el)
{
prev = p;
if (p.value < el)
p = p.right;
else
p = p.left;
}
node = p;
if (p != null && p.value == el)
{
if (node.right == null )
node = node.left;
else if (node.left == null )
node = node.right;
else
{
BSTIntNode temp = node.left;
BSTIntNode previous = node;
while (temp.right != null ) // 查找左字节数的最右子节点
{
previous = temp;
temp = temp.right;
}
node.value = temp.value;
if (previous == node)
previous.left = temp.left;
else
previous.right = temp.left;
}
if (p == root)
root = node;
else if (prev.left == p)
prev.left = node;
else
prev.right = node;
}
else if (root != null )
{
Console.WriteLine( " 没有找到节点:{0} " , el);
}
else
Console.WriteLine( " 树为空! " );
}
}
注意,在树中我们维持了一个Visit的delegate,看看使用方法:
public
static
void
Main(
string
[] args)
{
BSTIntTree tree = new BSTIntTree();
int []num = { 10 , 20 , 6 , 12 , 23 , 15 , 8 };
for ( int i = 0 ; i < num.Length; i ++ )
tree.Insert(num[i]);
// 添加遍历处理函数,可以有多个
tree.visit += new Visit(printNode);
Console.WriteLine( " 广度优先遍历 " );
tree.BreadthFirst();
Console.WriteLine( " 先序 " );
tree.PreOrder();
Console.WriteLine( " 中序 " );
tree.InOrder();
Console.WriteLine( " 后序 " );
tree.PostOrder();
tree.Delete( 8 );
tree.Delete( 15 );
Console.WriteLine( " 删除后广度优先遍历 " );
tree.BreadthFirst();
}
public static void printNode(BSTIntNode node)
{
Console.WriteLine( " 访问节点:{0} " , node.value);
}
{
BSTIntTree tree = new BSTIntTree();
int []num = { 10 , 20 , 6 , 12 , 23 , 15 , 8 };
for ( int i = 0 ; i < num.Length; i ++ )
tree.Insert(num[i]);
// 添加遍历处理函数,可以有多个
tree.visit += new Visit(printNode);
Console.WriteLine( " 广度优先遍历 " );
tree.BreadthFirst();
Console.WriteLine( " 先序 " );
tree.PreOrder();
Console.WriteLine( " 中序 " );
tree.InOrder();
Console.WriteLine( " 后序 " );
tree.PostOrder();
tree.Delete( 8 );
tree.Delete( 15 );
Console.WriteLine( " 删除后广度优先遍历 " );
tree.BreadthFirst();
}
public static void printNode(BSTIntNode node)
{
Console.WriteLine( " 访问节点:{0} " , node.value);
}
可以看到,C#的delegate机制非常有趣,如果在java中恐怕需要用inner class来实现了。