E118. Рандомизированная куча

e-maxx algorithm original: C/C++ #algorithm #data-structures #emaxx
Văn bản bài toán được dịch từ tiếng Nga theo ngôn ngữ giao diện. Mã không thay đổi.

Источник: 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ửi
using 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ốc
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;
}

Java lời giải

bản nháp tự động, xem lại trước khi gửi
import 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ị.

Tất cả việc làm
Chưa có việc làm đang hoạt động.