E118. Рандомизированная куча
Источник: e-maxx.ru/algo, страница PDF 391.
Рандомизированная куча (randomized heap) — это куча, которая за счёт Applications генератора случайных чисел позволяет выполнять все необходимые операции за логарифмическое ожидаемое время. Кучей называется бинарное cây, для любой вершины которого справедливо, что значение в этой вершине меньше либо равно значений во всех её потомках (это куча для минимума; разумеется, симметрично можно определить кучу для максимума). Таким образом, в корне кучи всегда находится минимум. Стандартный набор операций, определяемый для куч, следующий:
● Добавление elementа
● Нахождение минимума
● Извлечение минимума (удаление его из дерева и возврат его значения)
● Слияние двух куч (returnsся куча, содержащая elementы обеих куч; дубликаты не удаляются)
● Удаление произвольного elementа (при известной позиции в дереве)
Рандомизированная куча позволяет выполнять все эти операции за ожидаемое время
при очень
простой реализации.
Структура данных
Сразу опишем структуру данных, описывающую бинарную кучу:
struct tree {
T value;
tree * l, * r;
};
В вершине дерева хранится значение
некоторого типа
, для которого определён оператор
сравнения (
). Кроме того, хранятся указатели на левого и правого сыновей (которые равны 0, если соответствующий сын отсутствует).
Выполнение операций
Нетрудно понять, что все операции над кучей сводятся к одной операции: слиянию двух куч в одну. Действительно, добавление elementа в кучу равносильно слиянию этой кучи с кучей, состоящей из единственного добавляемого elementа. Нахождение минимума вообще не требует никаких действий — минимумом просто является корень кучи. Извлечение минимума эквивалентно тому, что куча заменяется результатом слияния левого и правого поддерева корня. Наконец, удаление произвольного elementа аналогично удалению минимума: всё подcây с корнем в этой вершине заменяется результатом слияния двух поддеревьев-сыновей этой вершины. Итак, нам фактически надо реализовать только операцию слияния двух куч, все остальные операции тривиально сводятся к этой операции.
Пусть given две кучи
и
, it is required вернуть их объединение. Понятно, что в корне каждой из этих куч находятся их минимумы, поэтому в корне результирующей кучи будет находиться минимум из этих двух значений. Итак, мы сравниваем, в корне какой из куч находится меньшее значение, его помещаем в корень результата, а теперь мы должны объединить сыновей выбранной вершины с оставшейся кучей. Если мы по какому-то признаку выберем одного из двух сыновей, то тогда нам надо будет просто объединить подcây в корне с этим сыном с кучей. Таким образом, мы снова пришли к операции слияния. Рано или поздно этот процесс остановится (на это понадобится, понятно, не более чем сумма высот куч). Таким образом, чтобы достичь логарифмической асимптотики в среднем, нам надо указать способ выбора одного из двух сыновей с тем, чтобы в среднем длина проходимого пути получалась бы порядка логарифма от количества elementов в куче. Нетрудно догадаться, что производить этот выбор мы будем случайно, таким образом, Cài đặt операции слияния получается такой:
tree * merge (tree * t1, tree * t2) {
if (!t1 || !t2)
return t1 ? t1 : t2;
if (t2->value < t1->value)
swap (t1, t2);
if (rand() & 1)
swap (t1->l, t1->r);
t1->l = merge (t1->l, t2);
return t1;
} Здесь сначала проверяется, если хотя бы одна из куч пуста, то никаких действий по слиянию производить не надо.
Иначе, мы делаем, чтобы куча
была кучей с меньшим значением в корне (для чего обмениваем
и
, если
надо). Наконец, мы считаем, что вторую кучу
будем сливать с левым сыном корня кучи
, поэтому мы
случайным образом обмениваем левого и правого сыновей, а затем выполняем слияние левого сына и второй кучи.
Asymptotic complexity
Введём случайную величину
, обозначающую длину случайного пути от корня до листа (длина в
числе рёбер). Понятно, что Thuật toán
выполняется за
операций. Поэтому
для исследования асимптотики Thuật toánа надо исследовать случайную величину .
Математическое ожидание
Утверждается, что математическое ожидание
оценивается сверху логарифмом от числа
вершин в этой куче:
Доказывается это легко по индукции. Пусть
и
— соответственно левое и правое поддеревья корня кучи
, а
и
— количества вершин в них (понятно, что
). Тогда справедливо:
что и требовалось доказать.
Превышение ожидаемой оценки
Докажем, что вероятность превышения полученной выше оценки мала:
для любой положительной константы
.
Обозначим через
множество путей от корня кучи до листьев, длина которых превосходит .
Заметим, что для любого пути
длины
вероятность того, в качестве случайного пути будет выбран именно он,
равна
. Тогда получаем: что и требовалось доказать.
Asymptotic complexity Thuật toánа
Таким образом, Thuật toán
, а, значит, и все остальные выраженные через него операции, выполняется
за
в среднем.
Более того, для любой положительной константы
найдётся такая положительная константа
, что вероятность того,
что операция потребует больше чем
операций, меньше
(это в некотором смысле описывает
худшее поведение Thuật toánа).
C# lời giải
bản nháp tự động, xem lại trước khi gửiusing System;
using System.Collections.Generic;
using System.Linq;
public static class AlgorithmDraft
{
// Auto-generated C# draft from the original e-maxx C/C++ listing. Review before production use.
struct tree {
T value;
tree * l, * r;
};
tree * merge (tree * t1, tree * t2) {
if (!t1 || !t2)
return t1 ? t1 : t2;
if (t2->value < t1->value)
swap (t1, t2);
if (rand() & 1)
swap (t1->l, t1->r);
t1->l = merge (t1->l, t2);
return t1;
}
}
C++ lời giải
đã khớp/gốcstruct tree {
T value;
tree * l, * r;
};
tree * merge (tree * t1, tree * t2) {
if (!t1 || !t2)
return t1 ? t1 : t2;
if (t2->value < t1->value)
swap (t1, t2);
if (rand() & 1)
swap (t1->l, t1->r);
t1->l = merge (t1->l, t2);
return t1;
}
Java lời giải
bản nháp tự động, xem lại trước khi gửiimport java.util.*;
import java.math.*;
public class AlgorithmDraft {
// Auto-generated Java draft from the original e-maxx C/C++ listing. Review before production use.
struct tree {
T value;
tree * l, * r;
};
tree * merge (tree * t1, tree * t2) {
if (!t1 || !t2)
return t1 ? t1 : t2;
if (t2->value < t1->value)
swap (t1, t2);
if (rand() & 1)
swap (t1->l, t1->r);
t1->l = merge (t1->l, t2);
return t1;
}
}
Материал разбит как Thuật toánическая Bài toán: изучить постановку, понять асимптотику и реализовать Thuật toán на выбранном языке.
Vacancies for this task
việc làm đang hoạt động with overlapping task tags are đã hiển thị.