C#使用泛型实现排序二叉树

什么是二叉树?
用一张图直观表示:
二叉树具有层级特效的数据结构。一棵树包含多个节点。
节点的层次属于二叉树的高,二叉树的效率衡量值由二叉树的高决定
排序二叉树(BST)特点:
    1、 子树上所有结点的值均 小于或等于 它的根结点的值
    2、 子树上所有结点的值均 大于或等于 它的根结点的值。
    3、 左、右子树也分别为二叉排序树。
顶部的节点称之为: 根节点,没有子树的节点称之为: 叶子节点

定义一个二叉树--C#实现:
采用泛型,适应性更广
public class Tree < TItem > where TItem : IComparable < TItem >
{
    private TItem NodeDate;//根节点
    private Tree< TItem > Left; //左子节点
    private Tree< TItem > Right; //右子节点
    /// <summary>
    /// 构造函数对树的对象复制
    /// </summary>
    /// <param name=" nodeValue "></param>
    public Tree( TItem nodeValue)
    {
        this .NodeDate = nodeValue;
        this .Right = null ;
        this .Left = null ;
    }
}
二叉树的 插入
原理:插入值与根节点做比较,若插入值大于根节点,则插入至根节点的右子树,若插入值小于根节点,则插入至左子树
C#代码实现:
/// <summary>
/// 树的插入操作,实现二叉排序树
/// </summary>
/// <param name=" newItem "></param>
public void Insert( TItem newItem)
{
    TItem currentNodeValue = this .NodeDate;
    //当currentNodeValue>newItem 即当前节点大于等于当前数值
    {
        //若当前左子树为空
        if ( this .Left == null )
        {
            //则当前左子树作为新的根节点
            this .Left = new Tree< TItem >(newItem);
        }
        else //否则继续判断
        {
            this .Left.Insert(newItem);
        }
    }
    else //根节点小于当前数值
    {
        if ( this .Right == null )
        {
            this .Right = new Tree< TItem >(newItem);
        }
        else
        {
            this .Right.Insert(newItem);
        }
    }
}


常见的 二叉树遍历 --原理:
1、中序遍历:
    访问顺序:首先访问左子树,然后访问根节点,最后访问右子树。
    实现逻辑:如果当前左子树不为空,则继续访问左子树,直到左子树的叶子节点,打印当前叶子节点,再打印其根节点,再访问其根节点的右子节点。
    适用:将数值从小到大排序
2、前序遍历:首先访问根节点,再访问左子树,最后访问右子树。
    适用:当已有一个二叉树的时候,需要将已有二叉树复制一份时,前序遍历效果最好。比重新构建二叉树的效率高了10倍左右
3、后序遍历:首先访问左子树,再访问右子树,最后访问根节点。
    适用:举例:操作系统的文件遍历时,若当前根节点存在子树,则代表存在子文件夹,若无子树,则代表当前文件夹为最后一个文件夹,即可访问当前文件夹内的内容
4、层序遍历
5、z-型层序遍历
(4、5在此不做讨论)
一、中序遍历--C#代码实现:
从根节点开始->左子树->右子树
public void TraverseTreeInOrder()
{
    //如果当前左子树不为空
    if ( this .Left != null )
    {
        //则继续查找左子树
        this .Left.TraverseTreeInOrder();
    }
    //当没有左子树时,打印当前根节点
    Console.WriteLine(this.NodeDate);
    //如果当前右子树不为空
    if ( this .Right != null )
    {
        //继续遍历右子树
        this .Right.TraverseTreeInOrder();
    }
}

二、前序遍历--C#代码实现:
从根节点开始->左子树->右子树
public void TraverseTreePerOrder()
{
    Console.WriteLine(this.NodeDate);
    if ( this .Left != null )
    {
        this .Left.TraverseTreePerOrder();
    }
    if ( this .Right != null )
    {
        this .Right.TraverseTreePerOrder();
    }
}

三、后续遍历--C#代码实现:
从左子树开始->右子树->根节点
public void TraverseTreeLastOrder()
{
    if ( this .Left != null )
    {
        this .Left.TraverseTreeLastOrder();
    }
    if ( this .Right != null )
    {
        this .Right.TraverseTreeLastOrder();
    }
    Console.WriteLine(this.NodeDate);
}


二叉树的 查找
原理:先判断当前节点是否存在,若存在,则用根节点与查找值左比较,若大于查找值,则进入左子树查询,若小于查找值,则进入右子树查找。存在则返回true,不存在则返回false
C#代码实现:
public bool TreeQuery(Tree< TItem > tree, TItem item)
{
    //如果当前节点为null,则代表不存在该数值
    if (tree == null )
    {
        return false ;
    }
    //如果NodeDate>item 当前节点大于当前数值
    if ( this . NodeDate.CompareTo(item ) > 0)
    {
        return TreeQuery(tree.Left, item);
    }//如果当前节点小于当前数值,则进入右子树查找
    else if ( this . NodeDate.CompareTo(item ) < 0)
    {
        return TreeQuery(tree.Right, item);
    }
    else
    {
        return true ;
    }
}

查找二叉树的最大值:
原理:二叉树的最大值位置在排序二叉树最右侧的节点。
先判断是当前节点是否存在,再判断当前节点是否存在右子树,若存在则进入右子树查询,一直访问至无右子树的节点,并返回此时(无右子树)的节点
C#代码实现:
public TItem TreeQueryMax(Tree<TItem> tree)
{
    if (tree != null )
    {
        while (tree != null && tree.Right != null )
        {
            tree = tree.Right;
        }
        return tree.NodeDate;
    }
    return default (TItem);
}

查找二叉树的最小值:
原理:二叉树的最小值位于排序二叉树的最左侧的节点。
先判断是当前节点是否存在,再判断当前节点是否存在左子树,若存在则进入左子树查询,一直找到无左子树的节点,并放回此时(无左子树)的节点。
public TItem TreeQueryMin(Tree<TItem> tree)
{
    if (tree != null )
    {
        while (tree != null && tree.Left != null )
        {
            tree = tree.Left;
        }
        return tree.NodeDate;
    }
    return default (TItem);
}

二叉树节点的删除:
原理:二叉树节点的删除分为三种情况
    第一种情况为:删除的节点没有左子树(删除下图的值为3的节点)。
    用3与根节点8做比较,3<8,此时则进入当前节点的左子树。此时3==3,那么将此节点删除。

将节点3删除时,用节点8的左孩子,指向被删除节点的左孩子。
即把当前节点(被删除的节点)的左子树,替换为当前节点。这样就不会破坏排序二叉树的平衡性。


第二种情况:删除的节点没有右子树(删除下图的值为10的节点)
原理:用10与根节点8做比较,因为10>8,进入右子树,此时10==10,那么将此节点删除。

将节点10删除时,用节点8的右子树指向被删除节点的右子树。


第三种情况:被删除的节点既包含了左子树,右包含了右子树。也是最复杂的情况,(删除下图值为3的节点)
原理:要删除节点3,从节点3的右子树(被选中的右子树中),找出最小节点,并用找出的最小节点,替换当前被删除的节点,节点3被替换后再删除替换节点4。

删除后,该排序二叉树依然保持平衡性。

第四种情况:是不是以为第三种情况那么复杂,第四种情况要上天了?经过复杂的脑回路思考,咱们休息一下。
    第四种情况为当前节点为叶子节点,那么直接删除就好了~~~是不是so easy?~

C#代码实现:
先定义一个查找右子树最小值的函数。(与二叉树查找最小数的方法略有区别,此函数返回的是最小节点的对象,查找二叉树最小值的方法放回的是一个数值)
public Tree<TItem> FindMinNode(Tree<TItem> tree, TItem item)
{
    if (tree != null )
    {
        while (tree != null && tree.Left != null )
        {
            tree = tree.Left;
        }
    }
    return tree;
}

public Tree<TItem> DeleteTreeNode(Tree<TItem> tree, TItem item)
{
    //如果当前节点为Null,则代表不包含此节点,删除失败
    if (tree == null )
    {
        return null ;
    }
    //如果当前值小于当前节点
    if ( tree.NodeDate.CompareTo(item ) > 0)
    {
        //进入左子树删除
        return DeleteTreeNode(tree.Left, item);
    }
    //如果值大于当前节点
    else if ( tree.NodeDate.CompareTo(item ) < 0)
    {
        //进入右子树删除
        return DeleteTreeNode(tree.Right, item);
    }
    //如果值匹配,则用右子树中的最小值替换该节点
    else
    {
        //判断该节点是否为叶子节点,若为叶子节点,则直接删除
        if (tree.Left == null && tree.Right == null )
        {
            tree = null ;
            return tree;
        }
        //如果当前节点只有右子树
        else if (tree.Left == null )
        {
            //则将当前节点的右子树换成当前节点
            tree = tree.Right;
            //返回被更新过的节点
            return tree;
        }
        //如果当前节点只有左子树
        else if (tree.Right == null )
        {
            //则将当前节点的左子树换成当前节点
            tree = tree.Right;
            //返回被更新过的节点
            return tree;
        }
        //如果当前节点左右子树均存在
        else if (tree.Right != null && tree.Left != null )
        {
            //找出右子树的最小节点,替换当前节点
            tree.NodeDate = FindMinNode(tree, item).NodeDate;
            //删除右子树的最小节点。
            tree.Left = DeleteTreeNode(tree.Right, tree.NodeDate);
            return tree;
        }
    }
    return null ;
}

上述所有方法,均写在  public class Tree < TItem > where TItem : IComparable<TItem>
最后简单说下二叉树的实例化、赋值以及调用。
class Program
{
    static void Main(string[] args)
    {
        Tree<int> t = new Tree<int>(8);//定义初始节点
        int[] a = { 3, 10, 1, 6, 4, 7, 14, 13 };
        //插入节点
        foreach (int item in a)
        {
            t.Insert(item);
        }
        Console.WriteLine("二叉树的打印");
        t.TraverseTreePerOrder();
        t.DeleteTreeNode(t, 3);
        Console.WriteLine("删除后,二叉树的打印");
        t.TraverseTreePerOrder();
        Console.Read();
    }
}















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值