Тема: Сегментне дерево
Сегментне дерево — це структура даних,
що надає можливість проведення двох операцій за O(log n) час:
обробки певного діапазону даних та оновлення елемента масива.
У нижченаведеному прикладі реалізовано сегментне дерево щодо пошуку максимума
для вирішення завдання https://leetcode.com/problems/fruits-in … cription/ .
Для більш детальної інформації щодо властивостей сегментного дерева
раджу прочитати розділ Range Queries 9 : 9.2.2 Segment Tree
з книги Laaksonen A. Guide to Competitive Programming 3ed 2024.
class SegmentTree {
// Сегментне дерево для пошуку максимума
public:
SegmentTree(const unsigned int arr_size_, const std::vector<int>& array_)
: arr_size(arr_size_)
, array(array_)
, tree_size(arr_size_ << 1U) // розмір вектора для сегментного дерева 2 * розмір
// оригінального вектора
{
seg_tree.resize(tree_size, 0);
// Заповнення листків сегментного дерева елементами з оригінального вектора
for (unsigned int i = (tree_size >> 1U), j = 0;
i < tree_size && j < arr_size; ++i, ++j) {
seg_tree[i] = array[j];
}
// Заповнення решти сегментного дерева за певною характеристикою:
// сумою, мінімумом, максимумом і т.д.
// Елемент у позиції 0 ігнорується
for (unsigned int i = (tree_size >> 1U) - 1; i >= 1; --i) {
seg_tree[i]
= std::ranges::max(seg_tree[i << 1U], seg_tree[(i << 1U) + 1]);
}
}
void update_tree(const int value, unsigned int pos)
{
// Додавання нового числового елемента до сегментного дерева та
// перезбирання його наново з урахуванням змін
pos += arr_size; // Перенесення позиції до листків дерева задля
// перезбирання знизу вгору
seg_tree[pos] = value;
while (pos > 1) {
pos >>= 1U;
seg_tree[pos] = std::ranges::max(seg_tree[pos << 1U],
seg_tree[(pos << 1U) + 1]);
}
}
auto find_max(unsigned int left, unsigned int right)
{
// Знаходження максимуму, працює лише для сегментних дерев побудованих
// для пошуку максимуму
left += arr_size; // Перенесення позицій до індексів листків дерева для
right += arr_size; // пошуку знизу вгору
int max_value { std::numeric_limits<int>::min() };
while (left <= right) {
// Властивість індексу лівої гілки — завжди парний
if ((left & 1U) == 1) {
max_value = std::ranges::max(max_value, seg_tree[left++]);
}
// Властивість індексу правої гілки — завжди непарний
if ((right & 1U) == 0) {
max_value = std::ranges::max(max_value, seg_tree[right--]);
}
left >>= 1U; // Ділення на 2, тобто перехід на вищий рівень дерева
right >>= 1U;
}
return max_value;
}
private:
unsigned int arr_size;
std::vector<int> array;
unsigned int tree_size;
std::vector<int> seg_tree;
};
class Solution {
public:
auto numOfUnplacedFruits(const std::vector<int>& fruits,
const std::vector<int>& baskets) -> int
{
int answer { 0 };
auto baskets_size
= static_cast<unsigned int>(std::ranges::size(baskets));
SegmentTree SegTree(baskets_size, baskets);
for (const auto& fruit : fruits) {
if (SegTree.find_max(0, baskets_size - 1) < fruit) {
++answer;
} else {
unsigned int low { 0 };
unsigned int high { baskets_size - 1 };
while (low < high) {
// low + (high - low)/2
unsigned int mid = (low & high) + ((low ^ high) >> 1U);
if (SegTree.find_max(low, mid) < fruit) {
low = mid + 1;
} else {
high = mid;
}
}
SegTree.update_tree(0, low);
}
}
return answer;
}
};