闲着无事,写写二叉查找树用C#的简单实现。
  二叉查找树是二叉树的一种特别类型,特点是小值在父节点的左边,其余值在其右边,对排序、值查找有很好的支持。据说应用很广泛,但是我还没在项目中用到过。
  1,结构分析
  树由节点组成,首先对节点结构进行分析。这里使用双向链表的思想来确定节点与节点间的关系。
    a,属性:
      1,父级节点
      2,左子节点
      3,右子节点
      4,节点数据
为了方便,还加上HasChild属性、重写ToString方法。下面是泛型代码:
public class TreeNode<T>
{
public T Data
{
get;
set;
}

public TreeNode<T> Parent
{
get;
set;
}

public TreeNode<T> Left
{
get;
set;
}

public TreeNode<T> Right
{
get;
set;
}

public bool HasChild
{
get
{
if (this.Left != null || this.Right != null)
{
return true;
}
return false;
}
}

public override string ToString()
{
return string.Format("Data:{0} Parent:{1} Left:{2} Right:{3} HasChild:{4}", Data,
Parent == null ? "null" : Parent.Data.ToString(), Left == null ? "null" : Left.Data.ToString(),
Right == null ? "null" : Right.Data.ToString(), HasChild.ToString());
}
}
  然后,对树本身结构进行分析。为了防止数据结构在外部被破坏,3个属性都为只读。
    a,私有字段:
      1,根节点
    a,只读属性:
      1,节点数量
      2,最大值
      3,最小值
    b,方法
      1,插入
      2,查找
      3,删除
      5,遍历
  2,算法分析
  插入:
    a,如果根节点为null,设为根节点。
    b,把根节点设为当前节点,如果新节点的值小于当前节点的值,把当前节点设为当前节点的左子节点。反侧,设为其右子节点。直到当前节点的左(右)子节点为null,把新节点插入到这个位置,并把新节点的父级节点设为当前节点。
    c,节点数量加1。
public void Insert(T item)
{
TreeNode
<T> node = new TreeNode<T>()
{
Data
= item
};
if (root == null)
{
root
= node;
}
else
{
TreeNode
<T> current = root;
while (current != null)
{
if (current.Data.CompareTo(item) > -1)
{
if (current.Left == null)
{
node.Parent
= current;
current.Left
= node;
current
= null;
}
else
{
current
= current.Left;
}
}
else
{
if (current.Right == null)
{
node.Parent
= current;
current.Right
= node;
current
= null;
}
else
{
current
= current.Right;
}
}
}
}
count
++;
}
  
  最大值、最小值:二叉查找树找最大值和最小值是再容易不过的事了,最底层最左的节点有最小值,最右的节点有最大值。
public T Min
{
get
{
TreeNode
<T> current = root;
while (current.Left != null)
{
current
= current.Left;
}
return current.Data;
}
}

public T Max
{
get
{
TreeNode
<T> current = root;
while (current.Right != null)
{
current
= current.Right;
}
return current.Data;
}
}
  
  查找特定值:
    a,把根节点设为当前节点,如果查找值等于当前节点的值,返回当前节点。
    b,如果查找的值小于当前节点的值,把当前节点设为其右子节点。反侧,设为其左子节点。直到当前节点为null,返回null。
public TreeNode<T> Find(T item)
{
TreeNode
<T> current = root;
while (!current.Data.Equals(item))
{
if (current.Data.CompareTo(item) < 0)
{
current
= current.Right;
}
else
{
current
= current.Left;
}
if (current == null)
{
return null;
}
}
return current;
}
  
  遍历:
  二叉查找树常用的3种遍历方法:中序遍历、先序遍历和后序遍历。中序遍历是按照值的升序顺序访问所有节点的,虽然我很想写清楚这3个方法,无赖语文不怎么好,直接上代码吧。
/// <summary>
/// 中序遍历
/// </summary>
/// <param name="node"></param>
private void InOrder(TreeNode<T> node)
{
if (node != null)
{
InOrder(node.Left);
Console.Write(node.Data.ToString()
+ " ");
InOrder(node.Right);
}
}

/// <summary>
/// 先序遍历
/// </summary>
/// <param name="node"></param>
private void PreOrder(TreeNode<T> node)
{
if (node != null)
{
Console.Write(node.Data.ToString()
+ " ");
InOrder(node.Left);
InOrder(node.Right);
}
}

/// <summary>
/// 后序遍历
/// </summary>
/// <param name="node"></param>
private void PostOrder(TreeNode<T> node)
{
if (node != null)
{
InOrder(node.Left);
InOrder(node.Right);
Console.Write(node.Data.ToString()
+ " ");
}
}
    
  删除:
  由于二叉查找树特殊的结构,删除时需要对树进行重构,因此删除算法是比较复杂的。
    a,要删除的节点如果没有子节点,直接删除(去除引用)。
    b,如果有一个子节点,用子节点顶替该节点位置。
    c,如果有两个子节点,使用中序遍历,用该节点的右子节点的最左节点顶替该节点位置。
    d,节点数量减1。
其实删除和顶替,还可以分解为更细的算法步骤,为了理解,所以就不说那么细,直接看代码:
public void Remove(T item)
{
TreeNode
<T> current = this.Find(item);
if (current == null)
{
return;
}
this.count--;
bool isLeftNode = false;
if (current != root)
{
isLeftNode
= current.Parent.Data.CompareTo(item) == 1;
}
if (current.HasChild)
{
if (current.Left != null && current.Right != null)
{
TreeNode
<T> temp = current.Right;
TreeNode
<T> node = current.Right;
while (temp != null)
{
node
= temp;
temp
= temp.Left;
}
if (node.HasChild)
{
node.Right.Parent
= node.Parent;
node.Parent.Left
= node.Right;
}
else
{
node.Parent.Left
= null;
}
node.Parent
= current.Parent;

if (node != current.Right)
{
node.Right
= current.Right;
current.Right.Parent
= node;
}

node.Left
= current.Left;
current.Left.Parent
= node;

if (current != root)
{
if (isLeftNode)
{
current.Parent.Left
= node;
}
else
{
current.Parent.Right
= node;
}
}
else
{
this.root = node;
}
}
else
{
if (current == root)
{
this.root = current.Left == null ? current.Right : current.Left;
}
else
{
if (isLeftNode)
{
current.Parent.Left
= current.Left == null ? current.Right : current.Left;
}
else
{
current.Parent.Right
= current.Right == null ? current.Left : current.Right;
}
}
}
}
else
{
if (current == this.root)
{
this.root = null;
}
else if (isLeftNode)
{
current.Parent.Left
= null;
}
else
{
current.Parent.Right
= null;
}
}
}