第十四章 数据结构的扩张

概要

本章节讨论的数据结构均是通过对红黑树的扩张所构造的。14.1介绍了动态集合上顺序统计操作的数据结构;14.2抽象出数据结构的扩张过程;14.3介绍了区间数。

14.1动态顺序统计

顺序统计的概念在《算法导论》的前面章节已经做过介绍,对于一个无序的集合,可以在O(n)时间内确定任何的顺序统计量。本章节介绍如何修改红黑树,使得可以在O(lgn)时间内确定任何的顺序统计量。包括在O(lgn)时间内计算一个元素的秩,即它在线性集合中的位置。
首先对红黑树的结点定义做修改,增加附加信息size。

    /// <summary>
    /// 红黑树结点
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class RbNode<T>
    {
        public T Key { get; set; }

        public int Size { get; set; }

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

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

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

        public COLOR Color { get; set; }
    }

一个结点的Size包括了该结点的左右孩子结点的Size和(包括该结点本身)。
即:x.Size = x.Left.Size + x.Right.Size + 1

在讨论删除和插入之后如何维护Size信息之前,先讨论两个运用,第一个是查找具有给定秩的元素,我们调用函数Os_Select(RbNode x, int i)来实现,这里的函数意义是在x为结点的子树中查找第i小的元素。
一个结点的Size并不能说明一个结点在集合中的秩,但若该节点为树的根结点,就调用Os_Select(tree.Root, int i)来求得第i小的元素。因为根结点的Size恰好反应了它的秩。以下是代码

        /// <summary>
        /// 查找给点秩的结点(递归版)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        public static int Os_Select(RbNode<int> x, int i)
        {
            var r = x.Left.Size + 1;
            if (r == i)
            {
                return r;
            }
            else if (i < r)
            {
                return Os_Select(x.Left, i);
            }
            else
            {
                return Os_Select(x.Right, r - i);
            }

        }

第二个应用是确定一个元素的秩,即返回一个元素在树中的秩。以下是代码

        /// <summary>
        /// 确定一个元素的秩
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static int Os_Rank(RbTree<int> t, RbNode<int> x)
        {
            var r = x.Left.Size + 1;
            var y = x;
            while (y != t.Root)
            {
                if (y == y.Parent.Right)
                {
                    r += y.Parent.Right.Size;
                }
                y = y.Parent;
            }
            return r;
        }

这个过程我们可以认为x的秩是中序遍历中排在x之前输出的结点的个数再加上1(x自身)。书中对该函数中的循环不等式分析在此略过。

讲完两个运用之后,再回过头来讨论对size属性的维护,分为插入和删除两个部分讨论。
在插入中主要分为两个阶段:
1.维护子树的规模
插入完一个结点后要对插入结点路径上的每个结点都增加Size属性,新结点Size为1。该过程代价为O(lgn)。
2.旋转后的Size属性修正
红黑树中插入完结点可能要对树的结构做修正,在此过程中至多进行两次旋转,因此在旋转中我们要更新Size属性,以下是代码(这里只放出左旋转)

        /// <summary>
        /// 左旋转(包含size维护)
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        public static void Letf_Rotate(RbTree<int> t, RbNode<int> x)
        {
            var y = x.Right;
            x.Right = y.Left;
            if (y.Right != t.Tnil)
            {
                y.Right.Parent = x;
            }
            x.Parent = y.Parent;
            if (x.Parent == t.Tnil)
            {
                t.Root = y;
            }
            else if (x.Parent.Left == x)
            {
                x.Parent.Left = y;
            }
            else
            {
                x.Parent.Right = y;
            }
            y.Left = x;
            x.Parent = y;
            y.Size = x.Size;
            x.Size = x.Left.Size + x.Right.Size + 1;
        }

在删除中同样分为两个阶段:
1.维护子树的规模
删除一个结点时候,我们要么结点y从树中删除,要么就是将y在树中的位置上移。为了更新子树的规模,我们要更新从y结点出发向上到达根结点的路径上所有结点的都减少Size属性。这个过程耗时为O(lgn)。
2.旋转后的Size属性修正
删除完结点同样可能要对树的结构做修正,在此过程中进行O(1)次旋转。所以对插入和删除来说维护结点的Size属性同样需要O(lgn)的时间。

14.1练习

14.1-3 写出OS-SELECT的非递归版本。

        /// <summary>
        /// 查找给点秩的结点(非递归版)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        public static RbNode<int> Os_Select_(RbNode<int> x, int i)
        {
            var r = x.Left.Size + 1;
            while (r != i)
            {
                if (i < r)
                {
                    x = x.Left;
                }
                else
                {
                    x = x.Right;
                    i = r - i;
                }
                r = x.Left.Size + 1;
            }
            return x;
        }

14.1-4 写出一个递归过程OS-KEY-RANK(T,k),以一棵顺序统计树T和一个关键字k作为输入,要求返回k在由T表示的动态集合中的秩。假设T的所有关键字都不相同。

        /// <summary>
        /// 返回t树中值为k的结点的秩
        /// </summary>
        /// <param name="t"></param>
        /// <param name="k"></param>
        /// <returns></returns>
        public static int Os_Key_Rank(RbTree<int> t, int k)
        {
            var x = t.Root;
            if (x.Key == k)
            {
                return x.Left.Size + 1;
            }
            else if (x.Key > k)
            {
                t.Root = x.Left;
                return Os_Key_Rank(t, k);
            }
            else
            {
                t.Root = x.Right;
                return x.Left.Size + 1 + Os_Key_Rank(t, k);
            }
        }

写完就有一个膈应,我如果不改变t.Root的值那么递归就无法进展下去。但是在看到别人的博文中对于此处的处理大多是OS-KEY-RANK(right[root[T]], k),这里我就有一个疑问了,如果你下次递归传入right[root[T]]那么参数不就错误了吗?right[root[T]]只是一个结点,OS-KEY-RANK函数中明确说明了传入的参数是树T。这点我一直很疑惑。
所以我在这里的处理还是对Root重新赋值,使得递归是在原来树的子树基础上继续展开,这样造成的结果就是一旦递归结束了,原来的树T的属性Root会改变。(我们在调用OS-KEY-RANK(t,k)时候此时的t为一课完整的树)因此在递归结束后可能要对t的Root做复原。
以上是我的一些疑惑和想法。

14.1-5 给定n个元素的顺序统计树中的一个元素x和一个自然数i,如何在O(lgn)的时间内确定x在该树线性序中的第i个后继点?
利用Os_Select(t,Os_Rank(t,x)+i)就可求得x元素的第i个后继点。Os_Rank(t,x)即求出x在树t中的秩。无论是调用Os_Rank还是Os_Select,都需要消耗O(lgn)的时间,因此可以确定上届为O(2lg(n))=O(lg(n))。

14.1-7 说明如何在O(nlgn)时间内,利用顺序统计树对大小为n的数组中的逆序对(见思考题2-4)进行计数。
略过了14.1-6,因为我觉得能掌握14.1-7,也就能解决14.1-6的问题。
现附上代码:

        /// <summary>
        /// 求数组中的逆序对对数
        /// </summary>
        /// <param name="array">数组</param>
        /// <returns></returns>
        public static int OS_Inversion(int[] array)
        {
            var count = 0;
            Node<int> z;
            Tree<int> t = new Tree<int>();
            for (var i = 0; i < array.Length; i++)
            {
                z = new Node<int>(array[i]);
                Node<int> y = null;
                var x = t.Root;
                while (x != null)
                {
                    y = x;
                    y.Size++;
                    if (x.key > z.key)
                    {
                        count += x.Right.Size + 1;
                        x = x.Left;
                    }
                    else
                    {
                        x = x.Right;
                    }
                }
                z.Parent = x;
                if (y == null)
                {
                    t.Root = z;
                }
                else if (y.key > z.key)
                {
                    y.Left = x;
                }
                else
                {
                    y.Right = z;
                }
            }
            return count;
        }

结合逆序对的定义,当我们往一棵树里面插入元素的时候,如果在插入路径中有结点的值大于我们插入的结点,那么等同于该结点的右子树个数(包括该结点本身)就是累加的逆序对个数,当插入完一个结点后,该路径上完成的累加数就是我们的逆序对对数。这个过程的上界就是O(nlgn),一共有n个待插入结点,每次插入耗时lg(n)。

*14.1-8 现有一个圆上的n条弦,每条弦都由其端点来定义。请给出一个能在O(lgn)时间内确定圆内相交弦对数的算法。
先附上代码,代码较长。主要把定义和一些扩展的红黑树基本操作也罗列了出来(正好算是验证基本操作的代码是否写得有问题),配合简单的例子来演示该算法。

    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<Line>
            {
                new Line(new Point(1, 15),
                              new Point(1, 160)),
                new Line(new Point(2, 100),
                              new Point(2, 330)),
                new Line(new Point(3, 35),
                              new Point(3, 280)),
                new Line(new Point(4, 85),
                              new Point(4, 300))
            };
            Console.WriteLine(Execute(list));
        }

        #region "求相交弦对数"
        public static int Execute(List<Line> list)
        {
            var count = 0;
            var t_s = new RbTree<Point>();
            var t_e = new RbTree<Point>();
            for (var i = 0; i < list.Count; i++)
            {
                var node_s = new RbNode<Point>(list[i].StartPoint);
                var node_e = new RbNode<Point>(list[i].EndPoint);
                node_e.Relative = node_s;
                RB_Insert(t_s, node_s);
                RB_Insert(t_e, node_e);
            }
            for (var i = 0; i < list.Count; i++)
            {
                var temp = OS_Select(t_e.Root, 1);
                var t_s_count = t_s.Root.Left.Size + t_s.Root.Right.Size + 1;
                count += t_s_count - OS_Rank(t_s, temp.Relative);
                RB_Delete(t_e, temp);
                RB_Delete(t_s, temp.Relative);
            }
            return count;
        }
        #endregion

        #region "定义"

        public class Line
        {
            public Point StartPoint { get; set; }

            public Point EndPoint { get; set; }

            public Line(Point p_s, Point p_e)
            {
                StartPoint = p_s;
                EndPoint = p_e;
            }
        }

        public class Point
        {
            public int Number { get; set; }

            public int Angle { get; set; }

            public Point(int number, int angle)
            {
                Number = number;
                Angle = angle;
            }
        }

        public class RbTree<T>
        {
            public RbNode<T> Tnil { get; set; }

            public RbNode<T> Root { get; set; }

            public RbTree()
            {
                Tnil = new RbNode<T>
                {
                    Color = COLOR.BLACK
                };
                Root = Tnil;
            }
        }

        /// <summary>
        /// 红黑树结点
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class RbNode<T>
        {
            public T Key { get; set; }

            public int Size { get; set; }

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

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

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

            public RbNode<T> Relative { get; set; }

            public COLOR Color { get; set; }

            public RbNode(T key)
            {
                Key = key;
            }

            public RbNode()
            {

            }
        }
        #endregion

        #region "扩展操作"
        /// <summary>
        /// 非递归版中序遍历
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        public static void Inorder_Tree_Walk(RbTree<Point> t, RbNode<Point> x)
        {
            var y = x;
            var s = new Stack<RbNode<Point>>();
            while (s.Count != 0
                    || y != t.Tnil)
            {
                while (y != t.Tnil)
                {
                    s.Push(y);
                    y = y.Left;
                }
                if (s.Count != 0)
                {
                    var temp = s.Pop();
                    Console.WriteLine(string.Format("No:{0},Value:{1},Color:{2}",
                                      temp.Key.Number, temp.Key.Angle, temp.Color.ToString()));
                    y = temp.Right;
                }
            }
        }

        /// <summary>
        /// 红黑树插入
        /// </summary>
        /// <param name="t"></param>
        /// <param name="z"></param>
        public static void RB_Insert(RbTree<Point> t, RbNode<Point> z)
        {
            RbNode<Point> y = t.Tnil;
            var x = t.Root;
            while (x != t.Tnil)
            {
                y = x;
                x.Size++;
                if (x.Key.Angle > z.Key.Angle)
                {
                    x = x.Left;
                }
                else
                {
                    x = x.Right;
                }
            }
            z.Parent = y;
            if (y == t.Tnil)
            {
                t.Root = z;
            }
            else if (y.Key.Angle > z.Key.Angle)
            {
                y.Left = z;
            }
            else
            {
                y.Right = z;
            }
            z.Size = 1;
            z.Color = COLOR.RED;
            z.Left = t.Tnil;
            z.Right = t.Tnil;
            Rb_Insert_Fix(t, z);
        }

        /// <summary>
        /// 红黑树插入修正
        /// </summary>
        /// <param name="t"></param>
        /// <param name="z"></param>
        private static void Rb_Insert_Fix(RbTree<Point> t, RbNode<Point> z)
        {
            while (z.Parent.Color != COLOR.BLACK)
            {
                var y = z.Parent.Parent;
                if (y.Left == z.Parent)
                {
                    // case1
                    if (y.Right.Color == COLOR.RED)
                    {
                        y.Color = COLOR.RED;
                        y.Right.Color = COLOR.BLACK;
                        y.Left.Color = COLOR.BLACK;
                        z = y;
                    }
                    else
                    {
                        // case2
                        if (z == z.Parent.Right)
                        {
                            z = z.Parent;
                            RB_Left_Rotate(t, z);
                        }
                        // case3
                        z.Parent.Color = COLOR.BLACK;
                        z.Parent.Parent.Color = COLOR.RED;
                        RB_Right_Rotate(t, z.Parent.Parent);
                    }
                }
                else
                {
                    // case1
                    if (y.Left.Color == COLOR.RED)
                    {
                        y.Color = COLOR.RED;
                        y.Left.Color = COLOR.BLACK;
                        y.Right.Color = COLOR.BLACK;
                        z = y;
                    }
                    else
                    {
                        // case2
                        if (z == z.Parent.Left)
                        {
                            z = z.Parent;
                            RB_Right_Rotate(t, z);
                        }
                        // case3
                        z.Parent.Color = COLOR.BLACK;
                        z.Parent.Parent.Color = COLOR.RED;
                        RB_Left_Rotate(t, z.Parent.Parent);
                    }
                }
            }
            t.Root.Color = COLOR.BLACK;
        }

        /// <summary>
        /// 红黑树结点删除
        /// </summary>
        /// <param name="t"></param>
        /// <param name="z"></param>
        private static void RB_Delete(RbTree<Point> t, RbNode<Point> z)
        {
            RbNode<Point> x;
            var y = z;
            var y_original_color = y.Color;
            if (z.Left == t.Tnil)
            {
                x = z.Right;
                RB_Transplant(t, z, z.Right);
            }
            else if (z.Right == t.Tnil)
            {
                x = z.Left;
                RB_Transplant(t, z, z.Right);
            }
            else
            {
                y = RB_Minimum(t, z.Right);
                y_original_color = y.Color;
                x = y.Right;
                if (y == z.Right)
                {
                    x.Parent = z;
                }
                else
                {
                    RB_Transplant(t, y, y.Right);
                    y.Right = z.Right;
                    y.Right.Parent = y;
                }
                RB_Transplant(t, z, y);
                y.Left = z.Left;
                y.Left.Parent = y;
                y.Color = z.Color;
                y.Size = z.Size;
                if (y_original_color == COLOR.BLACK)
                {
                    RB_Delete_Fix(t, x);
                }
            }
            var x_p = x.Parent;
            while (x_p != t.Tnil)
            {
                x_p.Size--;
                x_p = x_p.Parent;
            }
        }

        private static void RB_Delete_Fix(RbTree<Point> t, RbNode<Point> x)
        {
            while (x != t.Root
                    && x.Color == COLOR.BLACK)
            {
                if (x == x.Parent.Left)
                {
                    // case1
                    var w = x.Parent.Right;
                    if (w.Color == COLOR.RED)
                    {
                        w.Color = COLOR.BLACK;
                        x.Parent.Color = COLOR.RED;
                        RB_Left_Rotate(t, x.Parent);
                        w = x.Parent.Right;
                    }
                    // case2
                    if (w.Left.Color == COLOR.BLACK
                        && w.Right.Color == COLOR.BLACK)
                    {
                        x = x.Parent;
                        w.Color = COLOR.RED;
                    }
                    // case3
                    else if (w.Left.Color == COLOR.RED)
                    {
                        w.Left.Color = COLOR.BLACK;
                        w.Color = COLOR.RED;
                        RB_Right_Rotate(t, w);
                        w = x.Parent.Right;
                    }
                    // case4
                    w.Color = x.Parent.Color;
                    x.Parent.Color = COLOR.BLACK;
                    w.Right.Color = COLOR.BLACK;
                    RB_Left_Rotate(t, x.Parent);
                    x = t.Root;
                }
                else
                {
                    // case1
                    var w = x.Parent.Left;
                    if (w.Color == COLOR.RED)
                    {
                        w.Color = COLOR.BLACK;
                        x.Parent.Color = COLOR.RED;
                        RB_Right_Rotate(t, x.Parent);
                        w = x.Parent.Left;
                    }
                    // case2
                    if (w.Left.Color == COLOR.BLACK
                        && w.Right.Color == COLOR.BLACK)
                    {
                        x = x.Parent;
                        w.Color = COLOR.RED;
                    }
                    // case3
                    else if (w.Right.Color == COLOR.RED)
                    {
                        w.Right.Color = COLOR.BLACK;
                        w.Color = COLOR.RED;
                        RB_Left_Rotate(t, w);
                        w = x.Parent.Left;
                    }
                    // case4
                    w.Color = x.Parent.Color;
                    x.Parent.Color = COLOR.BLACK;
                    w.Left.Color = COLOR.BLACK;
                    RB_Right_Rotate(t, x.Parent);
                    x = t.Root;
                }
            }
            x.Color = COLOR.BLACK;
        }

        /// <summary>
        /// 红黑树的左转
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        private static void RB_Left_Rotate(RbTree<Point> t, RbNode<Point> x)
        {
            var y = x.Right;
            x.Right = y.Left;
            if (y.Left != t.Tnil)
            {
                y.Left.Parent = x;
            }
            y.Parent = x.Parent;
            if (x.Parent == t.Tnil)
            {
                t.Root = y;
            }
            else if (x.Parent.Left == x)
            {
                x.Parent.Left = y;
            }
            else
            {
                x.Parent.Right = y;
            }
            y.Size = x.Size;
            x.Size = x.Left.Size + x.Right.Size + 1;
            y.Left = x;
            x.Parent = y;
        }

        /// <summary>
        /// 红黑树的右转
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        private static void RB_Right_Rotate(RbTree<Point> t, RbNode<Point> x)
        {
            var y = x.Left;
            x.Left = y.Right;
            if (y.Right != t.Tnil)
            {
                y.Right.Parent = x;
            }
            y.Parent = x.Parent;
            if (x.Parent == t.Tnil)
            {
                t.Root = y;
            }
            else if (x.Parent.Left == x)
            {
                x.Parent.Left = y;
            }
            else
            {
                x.Parent.Right = y;
            }
            y.Size = x.Size;
            x.Size = x.Left.Size + x.Right.Size + 1;
            y.Right = x;
            x.Parent = y;
        }

        /// <summary>
        /// 红黑树结点替换
        /// </summary>
        /// <param name="t"></param>
        /// <param name="u"></param>
        /// <param name="v"></param>
        private static void RB_Transplant(RbTree<Point> t, RbNode<Point> u, RbNode<Point> v)
        {
            if (u.Parent == t.Tnil)
            {
                t.Root = v;
            }
            else if (u.Parent.Left == u)
            {
                u.Parent.Left = v;
            }
            else
            {
                u.Parent.Right = v;
            }
            v.Parent = u.Parent;
        }

        /// <summary>
        /// 当前结点为子红黑树的后继结点
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        private static RbNode<Point> RB_Minimum(RbTree<Point> t, RbNode<Point> x)
        {
            if (x.Right != t.Tnil)
            {
                while (x.Right.Left != t.Tnil)
                {
                    x = x.Left;
                }
                return x;
            }
            var y = x.Parent;
            while (y != t.Tnilfyg
                    && y == y.Parent.Right)
            {
                x = y;
                y = y.Parent;
            }
            return x;
        }

        /// <summary>
        /// 求一棵以x为头结点树的第i小结点
        /// </summary>
        /// <param name="x"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        private static RbNode<Point> OS_Select(RbNode<Point> x, int i)
        {
            var r = x.Left.Size + 1;
            if (r == i)
            {
                return x;
            }
            else if (i < r)
            {
                return OS_Select(x.Left, i);
            }
            else
            {
                return OS_Select(x.Right, i - r);
            }
        }

        /// <summary>
        /// 求红黑树中的结点的秩
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        private static int OS_Rank(RbTree<Point> t, RbNode<Point> x)
        {
            var r = x.Left.Size + 1;
            var y = x;
            while (y != t.Root)
            {
                if (y.Parent.Right == y)
                {
                    r += y.Parent.Left.Size + 1;
                }
                y = y.Parent;
            }
            return r;
        }

        #endregion
    }

调试了半小时总算将之前写的一些错误给纠正了,这里也得到了正确的答案。
讲一下简单的思路,这个思路也是借鉴了网上的一些答案,我原本的思路要比这些答案笨拙(虽然效率上来说是一样的)。因为没有看到比较好的代码,就顺手自己写了一份完整能运行的贴在上面。
在我的例子中弦一共有4条,每条弦的端点都不同,我们用端点到圆心和坐标轴之间的夹角来描述这些端点(也就是类Point中的Angle属性)
1)用两颗红黑树(t_s,t_e)分别插入一组弦的一侧端点。
2)插入完毕后获取t_e中的最小结点(也就是秩为1的端点),求得该端点后我们通过Realtive属性得到该弦另一侧的端点,另一个端点就存放在t_s中,之后我们求得另一侧端点中大于该端点的端点数。将该数目累加到变量count中。
3)当一次2)的处理完成后我们删去该条弦存放在两棵树中的相应端点。
这样子重复2)、3)过程直到两棵树都删至空。
这样得到的累加结果就是我们所需的对数。

14.2如何扩张数据结构

本节和下一节有着密切联系,书中写得过于繁琐可以直接结合视频讲解理解。
扩张一种数据结构可以分为4个步骤:
1)选择一种基础数据结构。
2)确定基础数据结构要维护的附加信息。
3)检验基础数据结构上的基本修改操作能否维护附加信息。
4)设计一个新操作。

14.1章中我们就是挑选了红黑树作为基础数据结构来扩张数据结构。OS_Select,OS_Rank就是我们为了满足新的需求所设计的操作(附加信息为属性SIze)。但有时候并不是为了一些新操作,而是利用附加信息来加速已有的操作。
下面的定理的证明就反应了红黑树当做数据结构,可以维护size属性。

定理14.1(红黑树的扩张) 设f是n个结点的红黑树T扩张的属性,且假设对任一结点x,f的值仅依赖于结点x、x.left、x.right的信息,还可能包括x.left.f和x.right.f。那我们可以在插入和删除操作期间对T的所有结点的f值进行维护,并且不影响这两个操作的O(lgn)渐近时间性能。

证明的主要思想是,对树中某结点x的f属性的变动只会影响到x的祖先。也就是说x.f只需要更新x.p.f,改变x.p.f的值只需要更新x.p.p.f,如此沿树向上。此过程直到向上至根结点结束。耗费时间为O(lgn)。
我们在14.1代码中也看到了,在插入和删除操作中维护size属性的值并不会影响该操作的好事上界O(lgn)。

14.2练习

14.2-1 通过为结点增加指针的方式,是说明如何在扩张的顺序统计树上,支持每一动态集合查找操作MINIMUM、MAXIMUM、SUCCESSOR、PREDECESSOR在最坏时间O(1)内完成。顺序统计树的其他操作的渐近性能不应受影响。

直接在结点中增加属性MAXIMUM(MINIMUM)、SUCCESSOR(PREDECESSOR)。
对于附加属性的维护主要在增加(删除)、左旋转(右旋转)中执行。
这里举个例子说明MAXIMUM(MAXIMUM)的情况,我们在结点中添加Max、Min属性。
在插入操作中,对插入结点经过的路径上的每个结点,我们都与之Max、Min属性进行比较, 若比Max的Key值大则更新当前结点的Max属性,Min同理。在旋转操作中我们也是在结点结构变更完之后对x、y的相应Max、Min属性进行更新。
删除操作中同样如此,对删除结点y的沿向上方向遍历,途中所有Key值大于y结点的祖先结点都更新Max属性,小于y的结点的更新Min属性。
以下是插入时的部分代码:

        /// <summary>
        /// 红黑树插入
        /// </summary>
        /// <param name="t"></param>
        /// <param name="z"></param>
        public static void RB_Insert(RbTree<int> t, RbNode<int> z)
        {
            RbNode<int> y = t.Tnil;
            var x = t.Root;
            while (x != t.Tnil)
            {
                y = x;
                x.Size++;
                if (x.Key > z.Key)
                {
                    if (x.Min.Key > z.Key)
                    {
                        x.Min = z;
                    }
                    x = x.Left;
                }
                else
                {
                    if (x.Max.Key < z.Key)
                    {
                        x.Max = z;
                    }
                    x = x.Right;
                }
            }
            z.Parent = y;
            if (y == t.Tnil)
            {
                t.Root = z;
            }
            else if (y.Key > z.Key)
            {
                y.Left = z;
            }
            else
            {
                y.Right = z;
            }
            z.Size = 1;
            z.Color = COLOR.RED;
            z.Left = t.Tnil;
            z.Right = t.Tnil;
            z.Max = z;
            z.Min = z;
            Rb_Insert_Fix(t, z);
        }
        
        /// <summary>
        /// 红黑树的左转
        /// </summary>
        /// <param name="t"></param>
        /// <param name="x"></param>
        private static void RB_Left_Rotate(RbTree<int> t, RbNode<int> x)
        {
            var y = x.Right;
            x.Right = y.Left;
            if (y.Left != t.Tnil)
            {
                y.Left.Parent = x;
            }
            y.Parent = x.Parent;
            if (x.Parent == t.Tnil)
            {
                t.Root = y;
            }
            else if (x.Parent.Left == x)
            {
                x.Parent.Left = y;
            }
            else
            {
                x.Parent.Right = y;
            }
            y.Size = x.Size;
            x.Size = x.Left.Size + x.Right.Size + 1;
            y.Left = x;
            x.Parent = y;
            x.Max = x.Right == t.Tnil ? x : x.Right.Max;
            y.Min = x.Min;
        }

14.2-2 能否在不影响任何操作的渐近性能的前提下,将结点的黑高作为树中结点的一个属性来维护?说明如何做,如果不能,请说明理由。如何维护结点的高度?

我的思路是结合14.1定理来证明能维护,假设x的黑高属性BHeight,在插入一个结点并在第二阶段做修复时候,我们在case1和case3都要对黑高度进行维护,case1时候是对当前结点x.parent和它的兄弟结点做黑高度维护,case3中对x.parent做维护。删除同理。

*14.2-3 设X为一个满足结合律的二元运算符,a为红黑树中每个结点上的一个要维护的属性。假设在每个结点x上增加一个属性f,使x.f = x1.a X x2.a X x3.a…Xxm.a,其中x1,x2,…,xm是以x为根的子树中按中序次序排列的所有结点。说明在一次旋转后,如何在O(1)时间内更新了f属性。对你的扩张稍做调整,使得它能够应用到顺序统计树的size属性中。

先说一下在O(1)时间内更新f属性:
根据已知条件我们能知道一个x为根的结点其f属性为 x.left.f X x.a X x.right.f
1)左旋情况下(x为旋转结点,y为x.right)
x.f = x.left.f X x.a X y.left.f
y.f = x.f X y.a X y.right.f
2)右旋情况下
x,f = y.right X x.a X x.right.f
y.f = y.left.f X y.a X x.f
注意到二元运算符X满足结合律但没说满足交换律,我们的式子不能随意交换运算顺序。
由上述可得在O(1)时间内能够完成旋转操作中属性f的更新。

该问题还要求我们扩张稍做调整,使得它能够应用到顺序统计树的size属性中。
暂时没有完整解答。

*14.2-4 希望设计一个操作RB_ENUMBERATE(x,a,b),来对红黑树进行扩张。该操作输出所有的关键字k,使得在以x为根的红黑树中有a<=k<=b。描述如何在O(m+lgn)时间内实现RB_ENUMERATE,其中m为输出的关键字数目,n为树中的内部结点数。(提示:不需要向红黑树中增加新的属性。)

搜索带关键字a的结点,若不存在则插入该结点,接着循环查找该结点的后续结点直到结点关键字大于b为止。

        public static void Rb_Enumerate(RbTree<int> t, RbNode<int> x,
                                            int a, int b)
        {
            RbNode<int> temp = null;
            var y = x;
            while (x != t.Tnil)
            {
                y = x;
                if (y.Key > a)
                {
                    x = x.Left;
                }
                else if (y.Key < a)
                {
                    x = x.Right;
                }
                else
                {
                    temp = x;
                }
            }
            if (temp == null)
            {
                temp = new RbNode<int>(a);
                RB_Insert(t, temp);
            }
            var sNode = RB_Successor(t, temp);
            while (sNode != t.Tnil
                   && sNode.Key <= b)
            {
                Console.Write("value:" + sNode.Key + " ");
                sNode = RB_Successor(t, sNode); ;
            }
            RB_Delete(t, temp);
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值