了解集合本质必须要知晓的概念04-二叉查找树

与链表、堆栈和队列不一样,二叉查找树不是线性数据结构,是二维数据结构。每个节点都包含一个LeftNode和RightNode,二叉查找树把比节点数据项小的数据放在LeftNode,把比节点数据项大的数据放在RightNode。

关于节点的类。

    public class TreeNode<T>
    {
        public T Element { get; set; }
        public TreeNode<T>  LeftNode { get; set; }
        public TreeNode<T>  RightNode { get; set; }

        public TreeNode(T element)
        {
            this.Element = element;
            LeftNode = RightNode = null;
        }

        public override string ToString()
        {
            string nodeString = "[" + this.Element + " ";

            if (this.LeftNode == null && this.RightNode == null)
            {
                nodeString += " (叶节点) ";
            }

            if (this.LeftNode != null)
            {
                nodeString += "左节点:" + this.LeftNode.ToString();
            }

            if (this.RightNode != null)
            {
                nodeString += "右节点:" + this.RightNode.ToString();
            }

            nodeString += "]";

            return nodeString;
        }
    }

以上,把比节点数据项Element小的数据所在节点赋值给LeftNode,把比节点数据项Element大的数据所在节点赋值给RightNode。

创建一个泛型二叉树查找类,维护着一个根节点,并提供各种对节点的操作方法。

    public class BinarySearchTree<T>
    {
        public TreeNode<T> Root { get; set; }

        public BinarySearchTree()
        {
            this.Root = null;
        }

        //把某个数据项插入到二叉树
        public void Insert(T x)
        {
            this.Root = Insert(x, this.Root);
        }

        //把某个数据项从二叉树中删除
        public void Remove(T x)
        {
            this.Root = Remove(x, this.Root);
        }

        //删除二叉树中的最小数据项
        public void RemoveMin()
        {
            this.Root = RemoveMin(this.Root);
        }

        //获取二叉树中的最小数据项
        public T FindMin()
        {
            return ElemntAt(FindMin(this.Root));
        }

        //获取二叉树中的最大数据项
        public T FindMax()
        {
            return ElemntAt(FindMax(this.Root));
        }

        //获取二叉树中的某个数据项
        public T Find(T x)
        {
            return ElemntAt(Find(x, this.Root));
        }

        //清空
        public void MakeEmpty()
        {
            this.Root = null;
        }

        //判断二叉树是否为空,是否存在
        public bool IsEmpty()
        {
            return this.Root == null;
        }

        //获取某个节点的数据项
        private T ElemntAt(TreeNode<T> t)
        {
            return t == null ? default(T) : t.Element;
        }

        /// <summary>
        /// 查找节点
        /// </summary>
        /// <param name="x">要查找数据项</param>
        /// <param name="t">已存在的节点</param>
        /// <returns>返回节点</returns>
        private TreeNode<T> Find(T x, TreeNode<T> t)
        {
            while (t != null)//当没有找到匹配数据项,不断调整查找范围,即t的值
            {
                if ((x as IComparable).CompareTo(t.Element) < 0)
                {
                    t = t.LeftNode;
                }
                else if ((x as IComparable).CompareTo(t.Element) > 0)
                {
                    t = t.RightNode;
                }
                else //如果找到数据项,就返回当前t的值
                {
                    return t;
                }
            }
            return null;
        }

        //获取最小的节点,
        private TreeNode<T> FindMin(TreeNode<T> t)
        {
            if (t != null)
            {
                while (t.LeftNode != null)//不断循环二叉树的左半边树
                {
                    t = t.LeftNode; //不断设置t的值
                }
            }
            return t;
        }

        //获取最大的节点
        private TreeNode<T> FindMax(TreeNode<T> t)
        {
            if (t != null)
            {
                while (t.RightNode != null)
                {
                    t = t.RightNode;
                }
            }
            return t;
        }

        /// <summary>
        /// 插入节点
        /// </summary>
        /// <param name="x">要插入的数据项</param>
        /// <param name="t">已经存在的节点</param>
        /// <returns>返回已存在的节点</returns>
        protected TreeNode<T> Insert(T x, TreeNode<T> t)
        {
            if (t == null)
            {
                t = new TreeNode<T>(x);
            }
            else if ((x as IComparable).CompareTo(t.Element) < 0)
            {
                //等号右边的t.LeftNode是null,因此会创建一个TreeNode实例给t.LeftNode
                t.LeftNode = Insert(x, t.LeftNode);
            }
            else if ((x as IComparable).CompareTo(t.Element) > 0)
            {
                t.RightNode = Insert(x, t.RightNode);
            }
            else
            {
                throw new Exception("插入了相同元素~~");
            }
            return t;
        }

        //删除最小的节点
        //返回当前根节点
        protected TreeNode<T> RemoveMin(TreeNode<T> t)
        {
            if (t == null)
            {
                throw new Exception("节点不存在~~");
            }
            else if (t.LeftNode != null)
            {
                //通过递归不断设置t.LeftNode,直到t.LeftNode=null
                t.LeftNode = RemoveMin(t.LeftNode);
                return t;
            }
            else //当t.LeftNode=null的时候,就把t.RightNode当作最小节点返回
            {
                return t.RightNode;
            }
        }

        //删除某数据项,返回当前根节点
        protected TreeNode<T> Remove(T x, TreeNode<T> t)
        {
            if (t == null)
            {
                throw new Exception("节点不存在~~");
            }
            else if((x as IComparable).CompareTo(t.Element) < 0)
            {
                t.LeftNode = Remove(x, t.LeftNode);
            }
            else if ((x as IComparable).CompareTo(t.Element) > 0)
            {
                t.RightNode = Remove(x, t.RightNode);
            }
            else if (t.LeftNode != null && t.RightNode != null)
            {
                t.Element = FindMin(t.RightNode).Element;
                t.RightNode = RemoveMin(t.RightNode);
            }
            else
            {
                t = (t.LeftNode != null) ? t.LeftNode : t.RightNode;
            }
            return t;
        }

        public override string ToString()
        {
            return this.Root.ToString();
        }
    }


客户端创建二叉查找树的实例,并调用实例方法插入随机数据。

            BinarySearchTree<int> intTree = new BinarySearchTree<int>();

            Random r = new Random(DateTime.Now.Millisecond);
            string trace = "";

            //插入5个随机数
            for (int i = 0; i < 5; i++)
            {
                int randomInt = r.Next(1, 500);
                intTree.Insert(randomInt);
                trace += randomInt + " ";
            }

            Console.WriteLine("最大的节点:" + intTree.FindMax());
            Console.WriteLine("最小的节点:" + intTree.FindMin());
            Console.WriteLine("根节点:" + intTree.Root.Element);
            Console.WriteLine("插入节点的依次顺序是:" + trace);
            Console.WriteLine("打印树为:" + intTree);
            Console.ReadKey();

参考资料:
Binary Search Trees (BSTs) in C#

“了解集合本质必须要知晓的概念”系列包括:

了解集合本质必须要知晓的概念01-链表

了解集合本质必须要知晓的概念02-堆栈

了解集合本质必须要知晓的概念03-队列

了解集合本质必须要知晓的概念04-二叉查找树

了解集合本质必须要知晓的概念04-二叉查找树

时间: 2024-10-10 03:01:41

了解集合本质必须要知晓的概念04-二叉查找树的相关文章

了解集合本质必须要知晓的概念03-队列

队列和堆栈都是约束版的链表,就像在超市购物,队列是先进先出的数据结构. 接着上一篇,派生于链表类List,来模拟一个队列. namespace LinkedListLibrary { public class QueueInheritance : List { public QueueInheritance() : base("queue"){} //入队:到最后面 public void Enqueue(object dataValue) { InsertAtBack(dataVal

了解集合本质必须要知晓的概念02-堆栈

在"了解集合本质必须要知晓的概念-链表"中,我们了解了链表的概念和种类,并且模拟了一个单向链表.本篇体验的堆栈是约束版的链表,只能在栈顶接收新节点和释放节点. 堆栈的主要操作是压栈和出栈.压栈是将新节点放在栈顶,出栈是从栈顶取出一个节点,返回新弹出节点的数据项.堆栈也称为后进先出的数据结构. 接着上一篇,写一个派生于List的类来模拟堆栈的压栈和出栈. namespace LinkedListLibrary { public class StackInheritance : List

十四、C# 支持标准查询运算符的集合接口

支持标准查询运算符的集合接口. System.Linq.Enumeralbe类提供的一些常用的API 来执行集合处理 1.匿名类型 2.隐匿类型的局部变量 3.集合初始化器 4.集合 5.标准查询运算符 本章主要讨论泛型集合接口. 非泛型的集合类,待查. 一.匿名类型和隐式类型的局部变量声明 C#3.0增强. 1.匿名类型 一种特殊的数据类型,它最终是由编译器声明的,而非通过已定义好的类来声明的. 和匿名函数相似,当编译器看到一个匿名类型时,会自动执行一些后台操作,生成必要的代码, 允许像显式声

解读Cardinality Estimation&lt;基数估计&gt;算法(第一部分:基本概念)

基数计数(cardinality counting)是实际应用中一种常见的计算场景,在数据分析.网络监控及数据库优化等领域都有相关需求.精确的基数计数算法由于种种原因,在面对大数据场景时往往力不从心,因此如何在误差可控的情况下对基数进行估计就显得十分重要.目前常见的基数估计算法有Linear Counting.LogLog Counting.HyperLogLog Counting及Adaptive Counting等.这几种算法都是基于概率统计理论所设计的概率算法,它们克服了精确基数计数算法的

面向对象 集合篇

集合概念: 集合本质上是一个动态数组 2.命令引用:using system.collections 2.1 Arratlist 属性(count) 数量 .属性2(capactity) 有几个位子 //集合里包括属性和方法2.2 arratlist list =new arraylist(); list.add(欲添加的值) object 类型的 为什么这里可以使object 类型的? 是因为object 是所有类型的父类,父类引用指向子类对象 所有类型都可以放在集合中去. 2.3 集合与数组

当你编码时你在做什么:谈编程的本质(一)状态机

当你编码时你在做什么:谈编程的本质(一)状态机 这学期学习了两门有意思的课,Theory of Computation和Distributed System,一低一高完全两个层次上的分支,却意外地发现两者在理论方面的重叠--那就是状态机.在Theory of Computation中,DFA.NFA.Turing Machine都是非常经典的State Machine,而在Distributed System中,Global State的确定.一致性协议的Replicated State Mac

小何讲进程: Linux进程的基本概念

1.  进程定义 进程是操作系统理论的核心与基础,操作系统中的许多概念都和进程相关. 进程的定义 ,进程有过各种各样的定义,现列举较为著名的几种. 进程是一个独立的可调度的活动: 进程是一个抽象实体,当它执行某个任务时,要分配和释放各种资源: 进程是可以并行执行的计算单位: 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动: 进程是一个程序的一次执行过程,同时也是资源分配的最小单元. 进程和程序是有本质区别的: 程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念

单点登录与权限管理本质:权限管理介绍

前面几篇文章介绍了单点登录的本质,包括cookie.session.重定向的基本概念,单点登录的基本交互流程,cookie的重要性和安全问题.单点登录能够确保:必须通过身份验证后,才能访问网站,且访问多个系统时,只需要登录一次. 一般系统都会有多个角色,不同角色可访问的系统功能不同,通过给用户分配不同角色,决定用户可访问的系统功能. 继续介绍「单点登录与权限管理」系列的第一部分:单点登录与权限管理本质,本篇说说权限管理,主要从以下几个方面介绍: 权限管理的一般模型 权限验证范围 Shiro基本架

集合、字符编码

一.集合 # pythoners=['王大炮','李二丫','陈独秀','艾里克斯','wxx','欧德博爱'] # linuxers=['陈独秀','wxx','egon','张全蛋'] # # l1=[] # for stu in pythoners: #将列表中的元素循环取出 # if stu in linuxers: #通过in判断stu是否在linuxers列表中 # # print(stu) # l1.append(stu) #如果pythoners列表中的元素在列表linuxers