二叉排序树的C#实现
2010-03-26 07:35
218 查看
寒假回家的时候,看了一下C#语言描述版的数据结构。我只是单纯的想,更加熟悉计算机语言,我希望能像是用母语一样使用计算机语言和计算机交流。很奇怪之前学C语言版本的数据结构,那些算法我都不是非常理解,但在读这本书的时候,却有种豁然开朗的感觉。是书写的好还是我能力有所提高亦或是两者皆有?
进入正题。
二叉排序树说起来其实并不是很难。二叉查找树是一种把值比较小的节点存储在左子节点内而值比较大的节点存储在右子节点里的树。其基本操作有以下几种:
[/code]
这里在比较的时候,我是在全局设置了一个与本地文化相关的Comparer类型的私有成员_comparer。这个类型用于对象判等。关键是你要判断的对象必须实现IComparable接口。我编写的TreeNode类型就实现了这个接口了。
如果找到匹配的节点,返回ture;否则,当前节点为Null,然后又不等于你要搜索的节点的值,那么就直接返回false。我的实现如下:
1、要删除的节点时叶子结点;这个情况很好解决,直接删掉就好了;
2、要删除的节点只有一个子节点;这个情况也很好解决,子节点替换一下父节点,问题解决;
2、要删除的节点有两个节点。这个就比较复杂了一些,但我们仔细分析,就会发现,要删除这个节点,只要利用中根遍历得到直接前驱或者直接后继结点代替该节点就可以了。同时,你还要删除前驱或者后继这个节点,而这两个节点,一定符合前面的两种情况之一。
以下是完整的代码,尽管是泛型,但其实我仅仅使用int类型编写
过测试。如果其他的类型,我想只要是实现了IComparable接口,应该就能正常工作。
进入正题。
二叉排序树说起来其实并不是很难。二叉查找树是一种把值比较小的节点存储在左子节点内而值比较大的节点存储在右子节点里的树。其基本操作有以下几种:
插入
我们对这个二叉树插入节点。如果二叉树本身没有任何节点,那么插入的这个节点就成为根节点;否则,根据定义,你应该遍历树,找出某一个节点,如果带插入节点的值大于这个节点,则成为带插入节点的右节点,否则就是左节点。从这里看来,根节点本身就是一个树的节点。因此,我首先实现了一个TreeNode类型,如下:接下来我们就能实现插入的功能了,通常我觉得好的代码是自描述的。也就是一段好的代码应该能自己描述自己的用途的。 [code]publicvoidAdd(TitemValue){
if(Root==null){
TreeNode<T>root=newTreeNode<T>(itemValue);
this.Root=root;
this.Count++;
}else{
TreeNode<T>_iterator=this.Root;
boolokFlag=true;;
intdeepness=2;
intresult=_comparer.Compare(itemValue,_iterator.Value);;
while(okFlag){
Debug.WriteLine("ComaperResultis:"+result.ToString());
Debug.WriteLine("---------------------------------------");
if(result>0){
if(_iterator.RightNode!=null){
_iterator=_iterator.RightNode;
result=_comparer.Compare(itemValue,_iterator.Value);
deepness++;
}else{
//在添加节点的时候就设置该节点的深度,而在计算整棵树的深度的时候,其实只要对所有叶节点的深度的值进行排序就可以得到了
_iterator.RightNode=newTreeNode<T>(itemValue,null,null,deepness);
_iterator.IsLeaf=false;
_iterator.RightNode.FatherNode=_iterator;
Count++;
okFlag=false;
}
}elseif(result<0){
if(_iterator.LeftNode!=null){
_iterator=_iterator.LeftNode;
result=_comparer.Compare(itemValue,_iterator.Value);
deepness++;
}else{
_iterator.LeftNode=newTreeNode<T>(itemValue,null,null,deepness);
_iterator.IsLeaf=false;
_iterator.LeftNode.FatherNode=_iterator;
Count++;
okFlag=false;
}
}else
okFlag=false;
}
}
}
[/code]
这里在比较的时候,我是在全局设置了一个与本地文化相关的Comparer类型的私有成员_comparer。这个类型用于对象判等。关键是你要判断的对象必须实现IComparable接口。我编写的TreeNode类型就实现了这个接口了。
查找
根据二叉搜索树的特点,如果你要搜索的节点的值比当前节点值小,那么就再搜索该节点的左子树;否则,搜索右子树。这个过程是递归过程。
如果找到匹配的节点,返回ture;否则,当前节点为Null,然后又不等于你要搜索的节点的值,那么就直接返回false。我的实现如下:
publicboolIsExit(Tkey,outTreeNode<T>node){
node=null;
TreeNode<T>_iterator=Root;
intresult=_comparer.Compare(key,_iterator.Value);
boolokFlag=true;
while(okFlag){
if(result==0){
okFlag=false;
node=_iterator;//如果找到这个叶子结点,那么得到叶子节点的指针
returntrue;
}elseif(result>0){
_iterator=_iterator.RightNode;
if(_iterator!=null)
result=_comparer.Compare(key,_iterator.Value);
else{
okFlag=false;
returnfalse;
}
}else{
_iterator=_iterator.LeftNode;
if(_iterator!=null)
result=_comparer.Compare(key,_iterator.Value);
else{
okFlag=false;
returnfalse;
}
}
}
returnfalse;
}
遍历
这个分三种情况的遍历,分别是前根遍历,中根遍历,后根遍历。其实很简单,就是按照访问节点的顺序来划分的。如果先访问左子节点,然后访问父节点,最后在访问右节点,这个情况就是前序遍历。其他的情况以此类推。这里我实现的时候,是用递归的方式。///<summary>
///中根遍历树
///</summary>
///<paramname="root"></param>
publicvoidInOrder(TreeNode<T>root){
if(root!=null){
InOrder(root.LeftNode);
Console.WriteLine(string.Format("Thisnode'svalueis{0}andit'sdeepnessis{1},leaf:{2}",root.ToString(),root.Deepness.ToString(),root.IsLeaf.ToString()));
InOrder(root.RightNode);
}
}
///<summary>
///先根遍历树
///</summary>
///<paramname="root"></param>
publicvoidPreOrder(TreeNode<T>root){
if(root!=null){
Console.WriteLine(string.Format("Thisnode'svalueis{0}andit'sdeepnessis{1},leaf:{2}",root.ToString(),root.Deepness.ToString(),root.IsLeaf.ToString()));
PreOrder(root.LeftNode);
PreOrder(root.RightNode);
}
}
///<summary>
///后根遍历树
///</summary>
///<paramname="root"></param>
publicvoidPostOrder(TreeNode<T>root){
if(root!=null){
PostOrder(root.LeftNode);
PostOrder(root.RightNode);
string.Format("Thisnode'svalueis{0}andit'sdeepnessis{1},leaf:{2}",root.ToString(),root.Deepness.ToString(),root.IsLeaf.ToString());
}
}
删除节点
作为这个树的实现里面最有难度的地方,要考虑三种情况。1、要删除的节点时叶子结点;这个情况很好解决,直接删掉就好了;
2、要删除的节点只有一个子节点;这个情况也很好解决,子节点替换一下父节点,问题解决;
2、要删除的节点有两个节点。这个就比较复杂了一些,但我们仔细分析,就会发现,要删除这个节点,只要利用中根遍历得到直接前驱或者直接后继结点代替该节点就可以了。同时,你还要删除前驱或者后继这个节点,而这两个节点,一定符合前面的两种情况之一。
publicvoidRemove(Tkey){
TreeNode<T>node;
boolisExit=IsExit(key,outnode);
if(!isExit){
return;
}else{
if(IsLeafNode(node)){
if(node.FatherNode.LeftNode==node)
node.FatherNode.LeftNode=null;
else
node.FatherNode.RightNode=null;
if(node!=null)node=null;
}else{
if(!HasTwoLeafNodes(node)){
TreeNode<T>child=GetUniqueChild(node);
if(node.FatherNode.LeftNode==node){
node.FatherNode.LeftNode=child;
}else{
if(node.LeftNode!=null)
node.FatherNode.RightNode=child;
}
if(node!=null)node=null;
}else{
//首先找到后继结点
TreeNode<T>successor=GetSuccessor(node);
//调整节点值,这个时候有一个值得注意的地方,比如你的树是这样的情况:
/*45或者:50
*\/
*5225
*/\/\
*/\1528
*4957/\/
*/\/\7826
*46505558
*\
*56
*/
//树A中节点52相应的后继结点应该是55,那么57的左子节点应调整成56,52就要调整成55
node.Value=successor.Value;
Remove(successor);
}
}
this.Count--;
}
}
以下是完整的代码,尽管是泛型,但其实我仅仅使用int类型编写
过测试。如果其他的类型,我想只要是实现了IComparable接口,应该就能正常工作。
usingSystem;
usingSystem.Collections;
usingSystem.Collections.Generic;
usingSystem.Globalization;
usingSystem.Diagnostics;
usingSystem.Text;
namespaceUltis{
publicclassBinaryTree<T>{
#region.ctor
staticBinaryTree(){
_comparer=newComparer(CultureInfo.CurrentCulture);
}
publicBinaryTree(){
this.Root=null;
this.Count=0;
this._deepness=0;
}
publicBinaryTree(TreeNode<T>root){
if(root==null){
thrownewArgumentException("Rootcannotbenull!!");
}
this.Root=root;
this.Count++;
this._deepness=1;
}
#endregion
#regionPublicMembers
///<summary>
///
///</summary>
///<paramname="itemValue"></param>
publicvoidAdd(TitemValue){
if(Root==null){
TreeNode<T>root=newTreeNode<T>(itemValue);
this.Root=root;
this.Count++;
}else{
TreeNode<T>_iterator=this.Root;
boolokFlag=true;;
intdeepness=2;
intresult=_comparer.Compare(itemValue,_iterator.Value);;
while(okFlag){
Debug.WriteLine("ComaperResultis:"+result.ToString());
Debug.WriteLine("---------------------------------------");
if(result>0){
if(_iterator.RightNode!=null){
_iterator=_iterator.RightNode;
result=_comparer.Compare(itemValue,_iterator.Value);
deepness++;
}else{
//在添加节点的时候就设置该节点的深度,而在计算整棵树的深度的时候,其实只要对所有叶节点的深度的值进行排序就可以得到了
_iterator.RightNode=newTreeNode<T>(itemValue,null,null,deepness);
_iterator.IsLeaf=false;
_iterator.RightNode.FatherNode=_iterator;
Count++;
okFlag=false;
}
}elseif(result<0){
if(_iterator.LeftNode!=null){
_iterator=_iterator.LeftNode;
result=_comparer.Compare(itemValue,_iterator.Value);
deepness++;
}else{
_iterator.LeftNode=newTreeNode<T>(itemValue,null,null,deepness);
_iterator.IsLeaf=false;
_iterator.LeftNode.FatherNode=_iterator;
Count++;
okFlag=false;
}
}else
okFlag=false;
}
}
}
///<summary>
///从树中移出一个节点,要考虑很多情况,比如要删除的节点没有子节点,有一个子节点,有两个子节点
///PS:这个方法应进行测试
///</summary>
/**
*删除指定的节点实现
*
*算法思想:
*
*1、若待删除的节点p是叶子节点,则直接删除该节点;
*
*2、若待删除的节点p只有一个子节点,则将p的子节点与p的父节点直接连接,然后删除节点p;
*为什么只有一个子节点时可以直接接到删除节点的父节点下面呢?因为只有一个子节点,直接接上
*去不会影响排序子节点本身的排序,当然更不会影响另外一个子树(因为另一子树跟本不存在!);
*
*3、若待删除节点p有两个子节点时,应该使用中序遍历方式得到的直接前置节点S或直接后继节点s
*的值来代替点s的值,然后删除节点s,(注:节点s肯定属于上述1、2情况之一)而不是直接删除
*p,这样可以将该删除问题转换成上面1、2问题;
*/
publicvoidRemove(Tkey){
TreeNode<T>node;
boolisExit=IsExit(key,outnode);
if(!isExit){
return;
}else{
if(IsLeafNode(node)){
if(node.FatherNode.LeftNode==node)
node.FatherNode.LeftNode=null;
else
node.FatherNode.RightNode=null;
if(node!=null)node=null;
}else{
if(!HasTwoLeafNodes(node)){
TreeNode<T>child=GetUniqueChild(node);
if(node.FatherNode.LeftNode==node){
node.FatherNode.LeftNode=child;
}else{
if(node.LeftNode!=null)
node.FatherNode.RightNode=child;
}
if(node!=null)node=null;
}else{
//首先找到后继结点
TreeNode<T>successor=GetSuccessor(node);
//调整节点值,这个时候有一个值得注意的地方,比如你的树是这样的情况:
/*45或者:50
*\/
*5225
*/\/\
*/\1528
*4957/\/
*/\/\7826
*46505558
*\
*56
*/
//树A中节点52相应的后继结点应该是55,那么57的左子节点应调整成56,52就要调整成55
node.Value=successor.Value;
Remove(successor);
}
}
this.Count--;
}
}
/**
*删除指定的节点实现
*
*算法思想:
*
*1、若待删除的节点p是叶子节点,则直接删除该节点;
*
*2、若待删除的节点p只有一个子节点,则将p的子节点与p的父节点直接连接,然后删除节点p;
*为什么只有一个子节点时可以直接接到删除节点的父节点下面呢?因为只有一个子节点,直接接上
*去不会影响排序子节点本身的排序,当然更不会影响另外一个子树(因为另一子树跟本不存在!);
*
*3、若待删除节点p有两个子节点时,应该使用中序遍历方式得到的直接前置节点S或直接后继节点s
*的值来代替点s的值,然后删除节点s,(注:节点s肯定属于上述1、2情况之一)而不是直接删除
*p,这样可以将该删除问题转换成上面1、2问题;
*/
publicvoidRemove(TreeNode<T>node){
if(IsLeafNode(node)){
if(node.FatherNode.LeftNode==node)
node.FatherNode.LeftNode=null;
else
node.FatherNode.RightNode=null;
if(node!=null)node=null;
}else{
if(!HasTwoLeafNodes(node)){
TreeNode<T>child=GetUniqueChild(node);
if(node.FatherNode.LeftNode==node){
node.FatherNode.LeftNode=child;
}else{
node.FatherNode.RightNode=child;
}
if(node!=null)node=null;
}else{
//要删除的节点有两个子节点
TreeNode<T>successor=GetSuccessor(node);
node.Value=successor.Value;
Remove(successor);//删除相应的后继结点
if(successor!=null)successor=null;
}
}
this.Count--;
}
///<summary>
///返回某一节点唯一的一个节点
///</summary>
///<paramname="node"></param>
///<returns></returns>
privateTreeNode<T>GetUniqueChild(TreeNode<T>node){
TreeNode<T>child;
if(node.LeftNode!=null)
child=node.LeftNode;
else
child=node.RightNode;
returnchild;
}
///<summary>
///根据中根遍历来得到相应的后继结点,仍然还有BUG存在!
///</summary>
///<paramname="value"></param>
///<returns></returns>
publicTreeNode<T>GetSuccessor(Tvalue){
thrownewNotImplementedException("Thisfunctionisnotcompleteyet!");
IComparableicomparable=RootasIComparable;
TreeNode<T>wanted=newTreeNode<T>(value);
if(icomparable.CompareTo(wanted)==0){
returnRoot;
}else{
TreeNode<T>node;
if(IsExit(value,outnode)){
if(IsLeafNode(node)){//如果是叶子节点,那么应该做么做才能返回正确的后继节点?
returnnull;
}else
returnGetSuccessor(node);//如果是非叶子节点,则调用相应的方法返回非叶子节点的后继结点
}else
returnnull;
}
}
///<summary>
///中根遍历树
///</summary>
///<paramname="root"></param>
publicvoidInOrder(TreeNode<T>root){
if(root!=null){
InOrder(root.LeftNode);
Console.WriteLine(string.Format("Thisnode'svalueis{0}andit'sdeepnessis{1},leaf:{2}",root.ToString(),root.Deepness.ToString(),root.IsLeaf.ToString()));
InOrder(root.RightNode);
}
}
///<summary>
///先根遍历树
///</summary>
///<paramname="root"></param>
publicvoidPreOrder(TreeNode<T>root){
if(root!=null){
Console.WriteLine(string.Format("Thisnode'svalueis{0}andit'sdeepnessis{1},leaf:{2}",root.ToString(),root.Deepness.ToString(),root.IsLeaf.ToString()));
PreOrder(root.LeftNode);
PreOrder(root.RightNode);
}
}
///<summary>
///后根遍历树
///</summary>
///<paramname="root"></param>
publicvoidPostOrder(TreeNode<T>root){
if(root!=null){
PostOrder(root.LeftNode);
PostOrder(root.RightNode);
string.Format("Thisnode'svalueis{0}andit'sdeepnessis{1},leaf:{2}",root.ToString(),root.Deepness.ToString(),root.IsLeaf.ToString());
}
}
///<summary>
///返回具有最大节点值的树节点
///</summary>
///<returns></returns>
publicTreeNode<T>GetMaxNode(){
TreeNode<T>_iterator=Root;
while(_iterator.RightNode!=null){
_iterator=_iterator.RightNode;
}
return_iterator;
}
///<summary>
///返回最大的值
///</summary>
///<returns></returns>
publicTGetMaxValue(){
returnGetMaxNode().Value;
}
///<summary>
///返回具有最小节点值得节点
///</summary>
///<returns></returns>
publicTreeNode<T>GetMinNode(){
TreeNode<T>_iterator=Root;
while(_iterator.LeftNode!=null){
_iterator=_iterator.LeftNode;
}
return_iterator;
}
///<summary>
///返回最小值
///</summary>
///<returns></returns>
publicTGetMinValue(){
returnGetMinNode().Value;
}
publicboolIsExit(Tkey,outTreeNode<T>node){
node=null;
TreeNode<T>_iterator=Root;
intresult=_comparer.Compare(key,_iterator.Value);
boolokFlag=true;
while(okFlag){
if(result==0){
okFlag=false;
node=_iterator;//如果找到这个叶子结点,那么得到叶子节点的指针
returntrue;
}elseif(result>0){
_iterator=_iterator.RightNode;
if(_iterator!=null)
result=_comparer.Compare(key,_iterator.Value);
else{
okFlag=false;
returnfalse;
}
}else{
_iterator=_iterator.LeftNode;
if(_iterator!=null)
result=_comparer.Compare(key,_iterator.Value);
else{
okFlag=false;
returnfalse;
}
}
}
returnfalse;
}
publicoverridestringToString(){
stringrtnString=string.Format("ThisisakindofbinarytreethatimpletedbyMaster,andithas{0}nodes.",Count.ToString());
returnrtnString;
}
publicTreeNode<T>Root{
get;
set;
}
publicintCount{
get;
privateset;
}
//返回树的深度
publicintDeepness{
get{
if(Count==1)
return1;
else{
int[]_deepnessSortArray=newint[Count];
intpointer=Count-1;
for(inti=0;i<Count;i++)_deepnessSortArray[i]=0;
InOrder(Root,refpointer,ref_deepnessSortArray);
Array.Sort<int>(_deepnessSortArray);//对_deepnessSortArray进行排序,得出其中最大的数值就是该树的深度
return_deepnessSortArray[Count-1];
}
}
}
#endregion
#regionPrivateMembers
privatestaticComparer_comparer;
privateint_deepness;
///<summary>
///遍历树节点,然后给深度数组_deepnessSortArray[i]赋值,之后对这个数组进行排序,得到的最大值就是该树的深度
///</summary>
///<paramname="root"></param>
///<paramname="pointer"></param>
///<paramname="deepnessSortArray"></param>
privatevoidInOrder(TreeNode<T>root,refintpointer,refint[]deepnessSortArray){
if(root!=null){
InOrder(root.LeftNode,refpointer,refdeepnessSortArray);
deepnessSortArray[pointer]=root.Deepness;
pointer--;
InOrder(root.RightNode,refpointer,refdeepnessSortArray);
}
}
///<summary>
///基于中根遍历的算法来得到某一个非叶子节点的后继结点
///</summary>
///<paramname="wannaDelNode"></param>
privateTreeNode<T>GetSuccessor(TreeNode<T>wannaDelNode){
TreeNode<T>_iterator=wannaDelNode.RightNode;
TreeNode<T>successorFather=null;
TreeNode<T>successor=null;
//Debug.WriteLine(string.Format("_iterator'svalueis{0}",_iterator.Value.ToString()));
//Debug.WriteLine(string.Format("successor'svalueis{0}",successor.Value.ToString()));
//首先应该要判断是不是叶子节点,如果是叶子节点,情况就完全不一样了
while(_iterator!=null){
successorFather=_iterator.FatherNode;
successor=_iterator;
_iterator=_iterator.LeftNode;
}
returnsuccessor;
}
privateboolIsLeafNode(TreeNode<T>node){
return(node.LeftNode==null&&node.RightNode==null)?true:false;
}
privateboolHasTwoLeafNodes(TreeNode<T>node){
return(node.LeftNode!=null&&node.RightNode!=null)?true:false;
}
#endregion
}
}
相关文章推荐
- 二叉排序树的C#实现
- 二叉排序树的实现(C#)
- C# 实现PNG文件的背景透明显示,解决动态显示闪烁问题 【转】
- 两副图片的差异比较的C#实现
- cocos2d-x系列笔记(9.1)---浅谈cocos2d-x使用函数指针实现委托(上)---通过c#代码理解委托
- C#实现的18位身份证格式验证算法
- js 实现 C# 的 format 方法
- 找项目网站----C# 下写入视频的简单实现
- C#实现在listview中插入图片实例代码
- C#实现把指定文件夹下的所有文件复制到指定路径下以及修改指定文件的后缀名
- 使用C#实现BST树的管理
- C#实现Ping服务器
- C#中使用repeater控件实现List的clear,add,update,delete功能之二数据库
- 《设计模式 基于C#的工程化实现及扩展》 - 书摘精要
- [C#]简单重写IComparer接口,实现自己的String.CompareTo 方法,自定义比较规则。
- Asp.net(C#)实现验证码功能
- <转>如何C#中实现在TreeView查找某一节点
- C# Winform DataGridView实现行[Row]的上下移动........
- C#实现的18位身份证格式验证算法[转载]
- C#实现大数据量TXT文本数据快速高效去重