Какую структуру данных, использующую O (n) хранилище с O (log n) временем запроса, я должен использовать для Range Minimum Queries? - PullRequest
22 голосов
/ 23 февраля 2010

Я озадачен следующим домашним заданием для класса алгоритмов:

Предположим, что нам дана последовательность из n значений x 1 , x 2 ... x n и стремиться к быстро ответить на повторные запросы Форма: учитывая I и J, найти наименьшее значение в x i ... x j

Разработка структуры данных, использующей O (n) пробел и ответы на запросы в O (log n) время.

Во-первых, я не уверен, относится ли sequence к отсортированному набору или к несортированному набору - но, поскольку в противном случае не будет указано, я буду считать, что sequence означает несортированное .

Итак, я понимаю, что это, очевидно, должно включать двоичное дерево, если мы говорим о времени поиска O (log N). В общем, я полагаю, у вас есть набор S, и вы вставляете каждый элемент в S в двоичное дерево. Проблема в том, что вопрос в основном хочет, чтобы я придумал способ ответить на запросы, где мне дали диапазон индексов в набор несортированный - и затем определить самое низкое значение в этом диапазоне в O ( журнал N) время. Как это возможно? Даже если каждый номер набора вставлен в дерево, лучшее, что я могу сделать, - это поиск любого конкретного числа за O (log N). Это не позволяет мне найти самое низкое значение в несортированном диапазоне чисел в S.

Есть предложения?

Ответы [ 6 ]

12 голосов
/ 23 февраля 2010

Если бы набор был отсортирован, вам не нужно дерево. Самый маленький элемент в диапазоне [i, j] будет иметь индекс i.

Итак, предположим, что элементы вашей последовательности были сохранены в порядке их индексов на листьях дерева. Можете ли вы хранить дополнительную информацию ( ahem , возможно, какое-то минимальное и максимальное значение) в каждом внутреннем узле, чтобы облегчить ваш запрос?

Если это так, то если дерево сбалансировано и если вы можете ответить на ваш запрос, посмотрев только на два пути от корня до двух элементов в {i, j}, то вы достигнете своего O (log N ) стоимость поиска. Поскольку сбалансированное бинарное дерево с N листьями содержит (2N-1) общих узлов, вы также будете удовлетворять пределу O (N) хранения.


ПОДРОБНОЕ ОПИСАНИЕ: рассмотрите возможность вычисления минимального значения в диапазоне [i, j].

В каждом внутреннем узле A дерева сохраняйте минимальное значение для всех листьев под ним. Это может быть вычислено снизу вверх при первом построении дерева.

Теперь начните с листа i. Идите вверх по дереву, сохраняя в качестве минимального значения вашего кандидата значение в i или что-либо, известное как правое от i и левое от j. Остановите один узел ниже общего предка i и j.

Начните снова с листа j. Поднимитесь вверх по дереву, еще раз сохранив в качестве кандидата минимальное значение в j или что-либо, о чем известно, что оно находится слева от j и справа от i.

Ваш минимум для [i, j] равен минимуму двух вычисленных вами значений. Вычисление максимума происходит аналогично. Общее требование к памяти составляет 2 значения для внутреннего узла плюс два указателя для внутреннего узла плюс одно значение для листа, что составляет N + 4 (N-1) для полного дерева.

Путь, по которому вы идете вверх по дереву из листа i, это тот же путь, по которому вы шли бы по дереву, если бы вы искали лист i.


C # КОД ДЛЯ ПОИСКА:

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

namespace RangeSearch
{
    public class RangeSearch
    {
        int[] tree;
        int N;

        int LeafLocation(int leafNumber) { return leafNumber + N - 1; }
        int LeafValue(int leafNumber) { return tree[ LeafLocation(leafNumber)]; }
        int LeftChild(int x) { return 2*x + 1; }
        int RightChild(int x) { return 2*x + 2; }
        int Parent(int x) { return (x-1)/2; }
        bool IsPowerOf2(int x) { while (x > 0) { if (x == 1) return true; if ((x & 1) == 1 ) return false; x = x >> 1; } return false; }
        bool IsAncestorOf( int x, int y ) { if( x>y ) return false;  return x==y || IsAncestorOf(LeftChild(x), y) || IsAncestorOf(RightChild(x),y); } // note: violating time bound for legibility, can fix by storing min/max descendant index at each node

        public RangeSearch(params int[] vals)
        {
            if (!IsPowerOf2(vals.Length))
                throw new ArgumentException("this implementation restricted to N being power of 2");
            N = vals.Length;
            tree = new int[2 * N - 1];
            // the right half of the array contains the leaves
            vals.CopyTo(tree, N - 1);
            // the left half of the array contains the interior nodes, each of which holds the minimum of all its children
            for (int i = N - 2; i >= 0; i--)
                tree[i] = Math.Min(tree[LeftChild(i)], tree[RightChild(i)]);           
        }

        public int FindMin(int a, int b)
        {
            if( a>b )
                throw new ArgumentException( "FindMin expects a range [a,b] with a<=b" );
            int x = Walk( a, true, b);
            int y = Walk( b, false, a);
            return Math.Min(x, y);
        }

        int Walk( int leafNumber, bool leftSide, int otherLeafNumber )
        {
            int minSoFar =  LeafValue(leafNumber);
            int leafLocation = LeafLocation(leafNumber);
            int otherLeafLocation = LeafLocation(otherLeafNumber);
            int parent = Parent(leafLocation);
            bool cameFromLeft = (leafLocation == LeftChild(parent));
            return Walk2(minSoFar, parent, cameFromLeft, leftSide, otherLeafLocation);
        }
        int Walk2(int minSoFar, int node, bool cameFromLeft, bool leftSide, int otherLeafLocation)
        {
            if (IsAncestorOf(node, otherLeafLocation))
                return minSoFar;
            if (leftSide)
                minSoFar = !cameFromLeft ? minSoFar : Math.Min(minSoFar, tree[RightChild(node)]);
            else
                minSoFar = cameFromLeft ? minSoFar : Math.Min(minSoFar, tree[LeftChild(node)]);
            return Walk2(minSoFar, Parent(node), node == LeftChild(Parent(node)), leftSide, otherLeafLocation);
        }
    }
}

C # КОД ДЛЯ ТЕСТИРОВАНИЯ:

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

namespace RangeSearch
{
    class Program
    {
        static void Main(string[] args)
        {
            RangeSearch rngA = new RangeSearch(9, 3, 7, 1);
            System.Diagnostics.Trace.Assert(3 == rngA.FindMin(0, 2) );
            System.Diagnostics.Trace.Assert(1 == rngA.FindMin(0, 3));
            System.Diagnostics.Trace.Assert(1 == rngA.FindMin(1, 3));

            RangeSearch rngB = new RangeSearch(1, 7, 3, 9);
            System.Diagnostics.Trace.Assert(3 == rngB.FindMin(1, 3));
            System.Diagnostics.Trace.Assert(1 == rngB.FindMin(0, 3));
            System.Diagnostics.Trace.Assert(1 == rngB.FindMin(0, 2));

            RangeSearch rngC = new RangeSearch(17, 21, 77, 70, 58, 79, 79, 89);
            System.Diagnostics.Trace.Assert(21 == rngC.FindMin(1, 7));

            RangeSearch rngD = new RangeSearch(94, 78, 88, 72, 95, 97, 89, 83);
            System.Diagnostics.Trace.Assert(72 == rngD.FindMin(1, 6));

            RangeSearch rngE = new RangeSearch(0, 66, 6, 43, 34, 34, 63, 49);
            System.Diagnostics.Trace.Assert(34 == rngE.FindMin(3, 4));

            Random rnd = new Random();
            for (int i = 0; i < 1000000; i++)
            {
                int[] tmp = new int[64];
                for (int j = 0; j < tmp.Length; j++)
                    tmp[j] = rnd.Next(0, 100);
                int a = rnd.Next(0, tmp.Length);
                int b = rnd.Next(a, tmp.Length);
                RangeSearch rng = new RangeSearch(tmp);
                System.Diagnostics.Trace.Assert(Min(tmp, a, b) == rng.FindMin(a, b));
            }
        }

        static int Min(int[] ar, int a, int b)
        {
            int x = ar[a];
            for (int i = a + 1; i <= b; i++)
                x = Math.Min(x, ar[i]);
            return x;
        }

    }
}
8 голосов
/ 23 февраля 2010

Хорошо, я думаю, что у меня есть хорошее начало для вас, и я узнал что-то новое в процессе.

Я бы взглянул на запись в Википедии о Декартовых деревьях . Я не собираюсь рассказывать тебе больше, потому что боишься делать за тебя домашнее задание, но ты выглядишь как умный парень, поэтому я думаю, ты сможешь решить это.

Кстати, спасибо за помощь в изучении новой структуры данных!

1 голос
/ 23 февраля 2010

Определение последовательность - это упорядоченный набор ( не отсортировано).

Знание того, что набор упорядочен , позволяет использовать декартово дерево , которое является идеальной структурой данных для Минимальный диапазон запросов .

0 голосов
/ 28 июля 2018

Вы можете использовать «Дерево сегментов» . В сегменте tress как обновление , так и запрос время равно O (logn). Вот ссылки, если вы хотите понять, как это работает.

  1. https://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/
  2. https://www.youtube.com/watch?v=ZBHKZF5w4YU
0 голосов
/ 05 июля 2013

Некоторые подталкивания:

Предположим, вы как-то сохранили минимум всех хорошо выровненных * подмассивов длиной 1, 2, 4, 8, ...? Как мало из этих минимумов вы можете избежать, чтобы посмотреть правильный ответ? Как вы можете хранить их, чтобы вы могли эффективно их извлекать?

(* например, мин магазина (x 0 ... 3 ) и мин (x 4 ... x 7 ), но не мин. (x 1 ... x 4 ))

0 голосов
/ 23 февраля 2010

Рассматривали ли вы интервальное дерево?

Глядя на запись в википедии, кажется, что она точно соответствует тому, что вы просите. http://en.wikipedia.org/wiki/Interval_tree

EDIT:

Да, кажется, что интервальные деревья не подходят для этого сценария ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...