代码改变世界

算法:二叉查询树

2017-09-05 18:18  huoit  阅读(258)  评论(0)    收藏  举报

对于定向等于式的查找我们知道Hash是效率最高的,时间复杂度为O(1);对于范围查找就需要使用二叉树

 

查询二叉树

查询树的【左节点<父节点,右节点>父节点】(小——父——大);

添加

假设一组随机数:200、100、300、80、150、210、320添加到树

首先树为空,拿到200后作为第一个节点;(第一个图)

再拿100;100<200所以往左走,左为空,直接存入左子树;

再拿300;300>200所以往右走,右为空,直接存入;(得到第二个图);

再拿80;80<200所有往左走,左不为空(存有节点100),再比较80<100往左走;为空,存入……递归到最后所有节点存入(第三个图)

 

最大

一定是最右边的叶子节点(忽略左子树:树的左侧都是小于父亲的,那就砍掉所有左侧)

最小

一定是最左边的叶子节点(忽略右子树:同理砍掉所有节点的右侧)

 

删除

删除的时候要保证这个树结构;左<父<右;

假设:删除300节点;同样根据大于小于当前节点递归查找树;找到等于300的节点后;

如果她只有一个孩子;直接提升位置到300的位置就可以了;

如果有两个孩子;找到右孩子里面最小的代替位置;然后删掉最小节点;

可以思考下:

1、找到该删除节点的右子树里面的最小值代替该节点的位置不破坏树结构;其他方式都会造成大量的重新生成树操作;或者破坏结构;

2、每个子树都是有取值范围的;某个节点(是顶级,是另一个爷爷节点的左节点,是另一个爷爷的右节点)

 

查找

做范围查找的时候;因为树的特点可以减少遍历的节点数目;

假设范围查找的范围为(min,max);递归每次遍历一个节点在范围内就取走;

1、拾取结果集【最小值(min)<节点值<最大值(max)】;

2、把整个树看作左右树;对于左树,从第一个小于下界限min的节点;的左树砍掉;对于右树第一个大于上界max;的右树都会被砍掉;从而优化了查询遍历的个数;

 

 

节点类:

using System;
using System.Collections.Generic;
using System.Text;

namespace BinaryTreeApp
{
    /// <summary>
    /// 二叉树节点
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="V"></typeparam>
    public class BinaryNode<K, V>
    {
        /// <summary>
        /// 节点元素
        /// </summary>
        public K key;

        /// <summary>
        /// 节点中的附加值
        /// </summary>
        public HashSet<V> attach = new HashSet<V>();

        /// <summary>
        /// 左节点
        /// </summary>
        public BinaryNode<K, V> left;

        /// <summary>
        /// 右节点
        /// </summary>
        public BinaryNode<K, V> right;

        public BinaryNode() { }

        public BinaryNode(K key, V value, BinaryNode<K, V> left, BinaryNode<K, V> right)
        {
            //KV键值对
            this.key = key;
            this.attach.Add(value);

            this.left = left;
            this.right = right;
        }
    }
}

 

查询二叉树类

using System;
using System.Collections.Generic;
using System.Text;

namespace BinaryTreeApp
{
    public class BinaryTree<K, V> where K : IComparable
    {
        public BinaryNode<K, V> node = null;

        #region 添加操作
        /// <summary>
        /// 添加操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Add(K key, V value)
        {
            node = Add(key, value, node);
        }
        /// <summary>
        /// 添加操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> Add(K key, V value, BinaryNode<K, V> tree)
        {
            if (tree == null)
                tree = new BinaryNode<K, V>(key, value, null, null);

            //左子树
            if (key.CompareTo(tree.key) < 0)
                tree.left = Add(key, value, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                tree.right = Add(key, value, tree.right);

            //将value追加到附加值中(也可对应重复元素)
            if (key.CompareTo(tree.key) == 0)
                tree.attach.Add(value);

            return tree;
        }
        #endregion

        #region 是否包含指定元素
        /// <summary>
        /// 是否包含指定元素
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Contain(K key)
        {
            return Contain(key, node);
        }

        /// <summary>
        /// 是否包含指定元素
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public bool Contain(K key, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return false;
            //左子树
            if (key.CompareTo(tree.key) < 0)
                return Contain(key, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                return Contain(key, tree.right);

            return true;
        }
        #endregion

        #region 树的指定范围查找
        /// <summary>
        /// 树的指定范围查找
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public HashSet<V> SearchRange(K min, K max)
        {
            HashSet<V> hashSet = new HashSet<V>();

            hashSet = SearchRange(min, max, hashSet, node);

            return hashSet;
        }
        
        /// <summary>
        /// 树的指定范围查找
        /// </summary>
        /// <param name="range1"></param>
        /// <param name="range2"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public HashSet<V> SearchRange(K min, K max, HashSet<V> hashSet, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return hashSet;

            //遍历左子树(寻找下界)
            if (min.CompareTo(tree.key) < 0)
                SearchRange(min, max, hashSet, tree.left);

            //当前节点是否在选定范围内
            if (min.CompareTo(tree.key) <= 0 && max.CompareTo(tree.key) >= 0)
            {
                //等于这种情况
                foreach (var item in tree.attach)
                    hashSet.Add(item);
            }

            //遍历右子树(两种情况:①:找min的下限 ②:必须在Max范围之内)
            if (min.CompareTo(tree.key) > 0 || max.CompareTo(tree.key) > 0)
                SearchRange(min, max, hashSet, tree.right);

            return hashSet;
        }
        #endregion

        #region 找到当前树的最小节点
        /// <summary>
        /// 找到当前树的最小节点
        /// </summary>
        /// <returns></returns>
        public BinaryNode<K, V> FindMin()
        {
            return FindMin(node);
        }
        
        /// <summary>
        /// 找到当前树的最小节点
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> FindMin(BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            if (tree.left == null)
                return tree;

            return FindMin(tree.left);
        }
        #endregion

        #region 找到当前树的最大节点
        /// <summary>
        /// 找到当前树的最大节点
        /// </summary>
        /// <returns></returns>
        public BinaryNode<K, V> FindMax()
        {
            return FindMax(node);
        }
       
        /// <summary>
        /// 找到当前树的最大节点
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> FindMax(BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            if (tree.right == null)
                return tree;

            return FindMax(tree.right);
        }
        #endregion

        #region 删除当前树中的节点
        /// <summary>
        /// 删除当前树中的节点
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public void Remove(K key, V value)
        {
            node = Remove(key, value, node);
        }
        
        /// <summary>
        /// 删除当前树中的节点
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> Remove(K key, V value, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            //左子树
            if (key.CompareTo(tree.key) < 0)
                tree.left = Remove(key, value, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                tree.right = Remove(key, value, tree.right);

            /*相等的情况*/
            if (key.CompareTo(tree.key) == 0)
            {
                //判断里面的HashSet是否有多值
                if (tree.attach.Count > 1)
                {
                    //实现惰性删除
                    tree.attach.Remove(value);
                }
                else
                {
                    //有两个孩子的情况
                    if (tree.left != null && tree.right != null)
                    {
                        //根据二叉树的中顺遍历,需要找到”有子树“的最小节点
                        tree.key = FindMin(tree.right).key;

                        //删除右子树的指定元素
                        tree.right = Remove(tree.key, value, tree.right);
                    }
                    else
                    {
                        //单个孩子的情况
                        tree = tree.left == null ? tree.right : tree.left;
                    }
                }
            }

            return tree;
        }
        #endregion
    }
}