Compare commits
2 Commits
9bdb691602
...
3c5d314e48
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c5d314e48 | ||
|
|
ec53ee7cab |
109
Algorithm/DivideOnTree/staticDivide.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 建议使用 N 定义数组,比 vector 稍快且容易管理
|
||||
const int N = 100005;
|
||||
vector<pair<int, int>> adj[N];
|
||||
bool vis[N]; // 分治标记:记录哪些点已经作为重心被“删掉”了
|
||||
int treesize[N];
|
||||
int root, min_maxpart, K;
|
||||
long long ans = 0;
|
||||
|
||||
// 存储当前子树所有节点到重心的距离
|
||||
vector<int> dists;
|
||||
|
||||
// 1. 找重心:注意这里不再需要 visited 数组,用 fa 限制方向即可
|
||||
void get_root(int u, int fa, int currsize) {
|
||||
treesize[u] = 1;
|
||||
int maxpart = 0;
|
||||
for (auto& edge : adj[u]) {
|
||||
int v = edge.first;
|
||||
if (v == fa || vis[v]) continue; // 关键:不能走已经“删掉”的点
|
||||
get_root(v, u, currsize);
|
||||
treesize[u] += treesize[v];
|
||||
maxpart = max(maxpart, treesize[v]);
|
||||
}
|
||||
maxpart = max(maxpart, currsize - treesize[u]);
|
||||
if (maxpart < min_maxpart) {
|
||||
min_maxpart = maxpart;
|
||||
root = u;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 收集距离:把所有节点到重心的距离存入 dists 数组
|
||||
void get_dists(int u, int fa, int d) {
|
||||
dists.push_back(d);
|
||||
for (auto& edge : adj[u]) {
|
||||
int v = edge.first;
|
||||
int w = edge.second;
|
||||
if (v == fa || vis[v]) continue;
|
||||
get_dists(v, u, d + w);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 计算贡献:双指针法统计满足 dist1 + dist2 <= K 的对数
|
||||
long long calc(int u, int init_dist) {
|
||||
dists.clear();
|
||||
get_dists(u, -1, init_dist);
|
||||
sort(dists.begin(), dists.end());
|
||||
|
||||
long long count = 0;
|
||||
int l = 0, r = dists.size() - 1;
|
||||
while (l < r) {
|
||||
if (dists[l] + dists[r] <= K) {
|
||||
count += (r - l);
|
||||
l++;
|
||||
} else {
|
||||
r--;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 4. 点分治主函数
|
||||
void divide(int u, int currsize) {
|
||||
// 每次进入先找当前连通块的重心
|
||||
min_maxpart = currsize + 7; // 初始化为一个大值
|
||||
get_root(u, -1, currsize);
|
||||
|
||||
int r = root; // 锁定重心
|
||||
vis[r] = true; // 【关键】逻辑切断重心
|
||||
|
||||
// 统计经过重心的路径
|
||||
ans += calc(r, 0);
|
||||
|
||||
// 递归子树
|
||||
for (auto& edge : adj[r]) {
|
||||
int v = edge.first;
|
||||
int w = edge.second;
|
||||
if (vis[v]) continue;
|
||||
|
||||
// 【关键】去重:减去在同一个儿子子树内提前满足条件的路径
|
||||
ans -= calc(v, w);
|
||||
|
||||
// 这里的 currsize 要更新为子树的实际大小
|
||||
// 注意:由于已经跑过 get_root,treesize[v] 此时就是正确的
|
||||
// 但如果 v 是 r 的“上方”节点,size 应该是 currsize - treesize[r]
|
||||
int next_size = (treesize[v] < treesize[r]) ? treesize[v] : (currsize - treesize[r]);
|
||||
divide(v, next_size);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
if (!(cin >> n >> K)) return 0;
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
int u, v, w;
|
||||
cin >> u >> v >> w;
|
||||
adj[u].push_back({v, w});
|
||||
adj[v].push_back({u, w});
|
||||
}
|
||||
|
||||
divide(1, n); // 从节点1开始分治,总大小为 n
|
||||
|
||||
cout << ans << endl;
|
||||
return 0;
|
||||
}
|
||||
153
Algorithm/积累/基础算法/二分/p1824进击的母牛.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# P1824 [USACO05FEB] 进击的奶牛 Aggressive Cows G
|
||||
|
||||
## 题目描述
|
||||
|
||||
农夫约翰建造了一座有 $n$ 间牛舍的小屋,牛舍排在一条直线上,第 $i$ 间牛舍在 $x_i$ 的位置,但是约翰的 $m$ 头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍。也就是要最大化最近的两头牛之间的距离。
|
||||
|
||||
牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗。为了不让牛互相伤害。约翰决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢?
|
||||
|
||||
## 输入格式
|
||||
|
||||
第一行用空格分隔的两个整数 $n$ 和 $m$;
|
||||
|
||||
下面 $n$ 行为 $n$ 个用空格隔开的整数,表示位置 $x_i$。
|
||||
|
||||
## 输出格式
|
||||
|
||||
一行一个整数,表示最大的最小距离值。
|
||||
|
||||
## 输入输出样例 #1
|
||||
|
||||
### 输入 #1
|
||||
|
||||
```
|
||||
5 3
|
||||
1
|
||||
2
|
||||
8
|
||||
4
|
||||
9
|
||||
```
|
||||
|
||||
### 输出 #1
|
||||
|
||||
```
|
||||
3
|
||||
```
|
||||
|
||||
## 说明/提示
|
||||
|
||||
【样例解析】把牛放在 $1$,$4$,$8$ 这三个位置,距离是 $3$。容易证明最小距离已经最大。
|
||||
|
||||
【数据范围】对于 $100\%$ 的数据,$2 \le n \le 10^5$,$0 \le x_i \le 10^9$,$2 \le m \le n$。不保证 $x$ 数组单调递增。
|
||||
|
||||
|
||||
哈哈,这种反应太正常了!初次接触这种题,谁都会觉得这种“反着来”的思路简直是脑洞大开。
|
||||
|
||||
这种思维方式在算法竞赛里有一个专门的套路,叫 **“二分答案”**。其实它不是靠灵光一现,而是有迹可循的“职业习惯”。
|
||||
|
||||
我们可以把这种思维过程拆解为三个“直觉点”,以后你看到类似的题,也能秒懂。
|
||||
|
||||
---
|
||||
|
||||
### 1. 关键词暗示:看到“最大化最小值”
|
||||
|
||||
当你看到题目要求:
|
||||
|
||||
* **最大化** 某个 **最小值**
|
||||
* 或者 **最小化** 某个 **最大值**
|
||||
|
||||
这在算法界几乎就是**明示**:请用二分搜索!
|
||||
|
||||
**为什么?** 因为直接计算“最优布局”非常困难,要考虑无数种组合。但如果我问你:“间距为 行不行?”你只需要从左往右摆摆看(贪心),一眼就能看出行不行。把“寻找题”变成“判断题”,难度瞬间降维。
|
||||
|
||||
### 2. 利用“单调性”:答案像个开关
|
||||
|
||||
这是最关键的直觉。你可以这样想:
|
||||
|
||||
* 如果间距是1 米,肯定能放下 头牛(太简单了)。
|
||||
* 如果间距是1000000 米,肯定放不下(太远了)。
|
||||
* 在这中间,一定存在一个**临界点**。在临界点左边,全是“行”;在临界点右边,全是“不行”。
|
||||
|
||||
既然有这种**单调性**(一半行,一半不行),我们就不需要一个个试,直接用**二分查找**去撞那个临界点。
|
||||
|
||||
### 3. 程序员的“偷懒”思维
|
||||
|
||||
正向思考(直接算位置)是“创造者思维”,很难;
|
||||
反向思考(猜一个答案去验证)是“检查者思维”,很简单。
|
||||
|
||||
**计算机最擅长做简单的重复劳动。** 既然验证一次只需要 ,二分查找只需要 次,总共也就跑 次验证。与其想破脑袋去算最优解,不如直接“暴力”地猜几十次。
|
||||
|
||||
---
|
||||
|
||||
### 总结一下这个“坑”是怎么跳进去的:
|
||||
|
||||
1. **第一步:** 看到“最大化最小间距”,心里默念:“二分答案预警”。
|
||||
2. **第二步:** 确定答案范围(比如 到 )。
|
||||
3. **第三步:** 写一个 `check` 函数。这一步通常用**贪心**。
|
||||
* *贪心逻辑:* 既然要间距大,我就把第一头牛放在最左边,后面每一头牛都紧贴着“当前位置 + 间距”能放下的第一个点放。
|
||||
|
||||
|
||||
4. **第四步:** 套二分模板。
|
||||
|
||||
> **这种感觉就像是:** 你去买衣服,不知道自己穿多大码。你不是拿尺子把自己全身量一遍再算缩水率(正向计算),而是直接拿 L 码试一下。大了?换 M。还大?换 S。这就是二分答案。
|
||||
|
||||
**你要不要试着写一下这个 `check` 函数的伪代码?我可以帮你看看逻辑对不对。**
|
||||
|
||||
|
||||
|
||||
``` cpp
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
//int ax[] = {1, 2, 4, 8, 9};
|
||||
|
||||
//按照分布,检查是否能够排列即可
|
||||
/*
|
||||
1、读入arr, mid
|
||||
2、按照分布排列,找到离mid格最近的槽位插入
|
||||
*/
|
||||
int check(const vector<int>& arr, int mid){
|
||||
if(arr.empty()) return 0;
|
||||
|
||||
int cnt = 1;
|
||||
int n = arr.size();
|
||||
int dist = 0;
|
||||
for(int i = 1; i < n; ++i){
|
||||
dist += arr[i] - arr[i - 1];
|
||||
if(dist >= mid){
|
||||
cnt++;
|
||||
dist = 0;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int main(){
|
||||
int n = 5, c = 3;
|
||||
cin >> n >> c;
|
||||
vector<int> arr(n);
|
||||
for(int i = 0; i < n; i++){
|
||||
cin >> arr[i];
|
||||
//arr[i] = ax[i];
|
||||
}
|
||||
sort(arr.begin(), arr.end());
|
||||
int smin = 0, smax = arr.back() - arr.front();
|
||||
//range [smin, smax], do mid to this range
|
||||
int l = smin, r = smax;
|
||||
int ans = 0;
|
||||
while(l <= r){
|
||||
int mid = (l+r)/2;
|
||||
int cc = check(arr, mid);
|
||||
if (cc >= c) {
|
||||
ans = mid;
|
||||
l = mid + 1;
|
||||
} else r = mid - 1;
|
||||
}
|
||||
cout << ans << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
using namespace std;
|
||||
/*
|
||||
* L Stands for Left Dividing Point, So as R
|
||||
* pos stands for: for each element, which Chunk the element is in
|
||||
* sum stands for the main purpose, what do we want the chunk to calculate
|
||||
*/
|
||||
vector<int> L, R, pos, sum, tag;
|
||||
void build(int n, const vector<int>& arr){
|
||||
int B = sqrt(n);
|
||||
int num = n / B; if(n % B) num++;
|
||||
L.resize(num); R.resize(num); sum.resize(num); tag.resize(num);
|
||||
for(int i = 0; i <= num; ++i) {
|
||||
L[i] = (i-1)*B + 1;
|
||||
R[i] = min(i*B, n); // Remind of The Last dec
|
||||
for(int j = L[i]; j <= R[i]; ++j){
|
||||
pos[j] = i;
|
||||
sum[i] += arr[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update(vector<int>& arr, int l, int r, int v){
|
||||
int lchk = pos[l], rchk = pos[r];
|
||||
//A. Only in one chunk
|
||||
if (lchk == rchk) {
|
||||
for(int i = l; i <= r; i++){
|
||||
arr[i] += v;
|
||||
sum[lchk] += v;
|
||||
}
|
||||
} else {
|
||||
//B. Across Chunks
|
||||
//left & right: violent update
|
||||
for(int i = l; i <= R[lchk]; ++i){
|
||||
arr[i] += v;
|
||||
sum[lchk] += v;
|
||||
}
|
||||
for(int i = L[rchk]; i <= r; ++i){
|
||||
arr[i] += v;
|
||||
sum[rchk] += v;
|
||||
}
|
||||
//middle: use tag
|
||||
for(int i = lchk+1; i <= rchk-1; ++i){
|
||||
tag[i] += v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long long query(const vector<int>& arr, int l, int r){
|
||||
int lchk = pos[l], rchk = pos[r];
|
||||
//A. Only in one chunk
|
||||
long long ans = 0;
|
||||
if (lchk == rchk) {
|
||||
for(int i = l; i <= r; i++){
|
||||
ans += arr[i] + tag[lchk];
|
||||
}
|
||||
} else {
|
||||
//B. Across Chunks
|
||||
//left & right: violent update
|
||||
for(int i = l; i <= R[lchk]; ++i){
|
||||
ans += arr[i] + tag[lchk];
|
||||
}
|
||||
for(int i = L[rchk]; i <= r; ++i){
|
||||
ans += arr[i] + tag[rchk];
|
||||
}
|
||||
//middle: use tag
|
||||
for(int i = lchk+1; i <= rchk-1; ++i){
|
||||
ans += sum[i] + tag[i] * (R[i]-L[i] + 1);
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
int main(){
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
48
Data-Structure/Tree/BinaryTree/BinaryIndexedTree/BIT.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
inline int lowbit(int x){
|
||||
return (x & (-x));
|
||||
}
|
||||
class BIT{
|
||||
public:
|
||||
vector<long long> tree; // 用 long long 防溢出
|
||||
int n;
|
||||
|
||||
BIT(int size) : n(size) {
|
||||
tree.resize(n+1, 0); // *BIT 空间通常是 n+1*
|
||||
}
|
||||
|
||||
BIT(vector<int> arr) {
|
||||
n = arr.size();
|
||||
tree.resize(n+1, 0);
|
||||
for(int i = 0; i < n; ++i){
|
||||
int x = i + 1;
|
||||
while(x <= n){
|
||||
tree[x] += arr[i];
|
||||
x += lowbit(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
//x: pos; d: alter a[x] to a[x] + d;
|
||||
void update(int x, int d) {
|
||||
if (x <= 0) return;
|
||||
while (x <= n) {
|
||||
tree[x] += d;
|
||||
x += lowbit(x);
|
||||
}
|
||||
}
|
||||
//return sum: a[0] + a[1] + ... + a[x]
|
||||
long long sum(int x) {
|
||||
long long ans = 0;
|
||||
while (x > 0) {
|
||||
ans += tree[x];
|
||||
x -= lowbit(x);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
};
|
||||
|
||||
int main(){
|
||||
|
||||
}
|
||||
91
Data-Structure/Tree/BinaryTree/K-DTree/2D-KDTree.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
/* ========= 2D KD-Tree ========= */
|
||||
|
||||
struct Point {
|
||||
double x, y;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Point p;
|
||||
Node *left, *right;
|
||||
Node(Point _p) : p(_p), left(nullptr), right(nullptr) {}
|
||||
};
|
||||
|
||||
double dist2(const Point& a, const Point& b) {
|
||||
double dx = a.x - b.x;
|
||||
double dy = a.y - b.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
Node* build(vector<Point>& pts, int l, int r, int depth) {
|
||||
if (l >= r) return nullptr;
|
||||
|
||||
int dim = depth % 2;
|
||||
int mid = (l + r) / 2;
|
||||
|
||||
nth_element(pts.begin() + l, pts.begin() + mid, pts.begin() + r,
|
||||
[&](const Point& a, const Point& b) {
|
||||
return dim == 0 ? a.x < b.x : a.y < b.y;
|
||||
});
|
||||
|
||||
Node* node = new Node(pts[mid]);
|
||||
node->left = build(pts, l, mid, depth + 1);
|
||||
node->right = build(pts, mid + 1, r, depth + 1);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void nearest(Node* node, const Point& target, int depth,
|
||||
Point& best, double& bestDist) {
|
||||
if (!node) return;
|
||||
|
||||
double d = dist2(node->p, target);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = node->p;
|
||||
}
|
||||
|
||||
int dim = depth % 2;
|
||||
Node *nearChild, *farChild;
|
||||
|
||||
if ((dim == 0 && target.x < node->p.x) ||
|
||||
(dim == 1 && target.y < node->p.y)) {
|
||||
nearChild = node->left;
|
||||
farChild = node->right;
|
||||
} else {
|
||||
nearChild = node->right;
|
||||
farChild = node->left;
|
||||
}
|
||||
|
||||
nearest(nearChild, target, depth + 1, best, bestDist);
|
||||
|
||||
double diff = (dim == 0)
|
||||
? target.x - node->p.x
|
||||
: target.y - node->p.y;
|
||||
|
||||
if (diff * diff < bestDist) {
|
||||
nearest(farChild, target, depth + 1, best, bestDist);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
vector<Point> points = {
|
||||
{2,3}, {5,4}, {9,6},
|
||||
{4,7}, {8,1}, {7,2}
|
||||
};
|
||||
|
||||
Node* root = build(points, 0, points.size(), 0);
|
||||
|
||||
Point target{9, 2};
|
||||
Point best;
|
||||
double bestDist = 1e18;
|
||||
|
||||
nearest(root, target, 0, best, bestDist);
|
||||
|
||||
cout << "Nearest point: (" << best.x << ", " << best.y << ")\n";
|
||||
cout << "Squared distance: " << bestDist << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
99
Data-Structure/Tree/BinaryTree/K-DTree/3D-KDTree.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
/* ========= 3D KD-Tree ========= */
|
||||
|
||||
struct Point {
|
||||
double x, y, z;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Point p;
|
||||
Node *left, *right;
|
||||
Node(Point _p) : p(_p), left(nullptr), right(nullptr) {}
|
||||
};
|
||||
|
||||
double dist2(const Point& a, const Point& b) {
|
||||
double dx = a.x - b.x;
|
||||
double dy = a.y - b.y;
|
||||
double dz = a.z - b.z;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
Node* build(vector<Point>& pts, int l, int r, int depth) {
|
||||
if (l >= r) return nullptr;
|
||||
|
||||
int dim = depth % 3;
|
||||
int mid = (l + r) / 2;
|
||||
|
||||
nth_element(pts.begin() + l, pts.begin() + mid, pts.begin() + r,
|
||||
[&](const Point& a, const Point& b) {
|
||||
if (dim == 0) return a.x < b.x;
|
||||
if (dim == 1) return a.y < b.y;
|
||||
return a.z < b.z;
|
||||
});
|
||||
|
||||
Node* node = new Node(pts[mid]);
|
||||
node->left = build(pts, l, mid, depth + 1);
|
||||
node->right = build(pts, mid + 1, r, depth + 1);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void nearest(Node* node, const Point& target, int depth,
|
||||
Point& best, double& bestDist) {
|
||||
if (!node) return;
|
||||
|
||||
double d = dist2(node->p, target);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = node->p;
|
||||
}
|
||||
|
||||
int dim = depth % 3;
|
||||
Node *nearChild, *farChild;
|
||||
|
||||
if ((dim == 0 && target.x < node->p.x) ||
|
||||
(dim == 1 && target.y < node->p.y) ||
|
||||
(dim == 2 && target.z < node->p.z)) {
|
||||
nearChild = node->left;
|
||||
farChild = node->right;
|
||||
} else {
|
||||
nearChild = node->right;
|
||||
farChild = node->left;
|
||||
}
|
||||
|
||||
nearest(nearChild, target, depth + 1, best, bestDist);
|
||||
|
||||
double diff = (dim == 0) ? target.x - node->p.x
|
||||
: (dim == 1) ? target.y - node->p.y
|
||||
: target.z - node->p.z;
|
||||
|
||||
if (diff * diff < bestDist) {
|
||||
nearest(farChild, target, depth + 1, best, bestDist);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
vector<Point> points = {
|
||||
{2,3,4}, {5,4,2}, {9,6,7},
|
||||
{4,7,9}, {8,1,5}, {7,2,6}
|
||||
};
|
||||
|
||||
Node* root = build(points, 0, points.size(), 0);
|
||||
|
||||
Point target{9, 2, 6};
|
||||
Point best;
|
||||
double bestDist = 1e18;
|
||||
|
||||
nearest(root, target, 0, best, bestDist);
|
||||
|
||||
cout << "Nearest point: ("
|
||||
<< best.x << ", "
|
||||
<< best.y << ", "
|
||||
<< best.z << ")\n";
|
||||
|
||||
cout << "Squared distance: " << bestDist << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
325
Data-Structure/Tree/BinaryTree/K-DTree/Readme.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# 1️⃣ What is a K-D Tree (clean & precise)
|
||||
|
||||
A **K-D Tree (k-dimensional tree)** is a **binary tree for organizing points in k-dimensional space**.
|
||||
|
||||
Each node:
|
||||
|
||||
* Stores **one point** `(x₀, x₁, …, xₖ₋₁)`
|
||||
* Splits space using **one dimension**
|
||||
* Left/right child represent **two half-spaces**
|
||||
|
||||
### Core rule (the soul of KD-Tree)
|
||||
|
||||
At depth `d`:
|
||||
|
||||
```
|
||||
split_dimension = d % k
|
||||
```
|
||||
|
||||
* Left subtree: points with smaller coordinate on that dimension
|
||||
* Right subtree: points with larger coordinate on that dimension
|
||||
|
||||
So the split dimension **cycles**:
|
||||
|
||||
```
|
||||
2D: x → y → x → y → ...
|
||||
3D: x → y → z → x → ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Why KD-Tree exists (not just “cool BST”)
|
||||
|
||||
KD-Tree is designed for **spatial queries**, not sorting.
|
||||
|
||||
It excels at:
|
||||
|
||||
* Nearest Neighbor (NN)
|
||||
* k-Nearest Neighbor (kNN)
|
||||
* Range / box queries
|
||||
|
||||
Why?
|
||||
➡️ Because it **partitions space**, allowing **whole subtrees to be pruned**.
|
||||
|
||||
---
|
||||
|
||||
## Balanced construction (important)
|
||||
|
||||
Given all points beforehand:
|
||||
|
||||
1. Choose split dimension
|
||||
2. Sort points by that dimension
|
||||
3. Take **median** as root
|
||||
4. Recurse on left / right
|
||||
|
||||
This gives:
|
||||
|
||||
* Height ≈ `O(log n)`
|
||||
* Good pruning behavior
|
||||
|
||||
This is the version we’ll implement.
|
||||
|
||||
---
|
||||
|
||||
## Geometry intuition (very important)
|
||||
|
||||
* Each node represents a **hyperplane**
|
||||
* Each subtree represents a **region of space**
|
||||
* Distance queries:
|
||||
|
||||
* First go to the “likely” side
|
||||
* Then check if the other side **can possibly contain a closer point**
|
||||
|
||||
That’s the entire NN trick.
|
||||
|
||||
---
|
||||
|
||||
# 2️⃣ 2D KD-Tree (C++ Template)
|
||||
|
||||
### Supported:
|
||||
|
||||
* Build from points
|
||||
* Nearest Neighbor query
|
||||
|
||||
---
|
||||
|
||||
### Data structures
|
||||
|
||||
```cpp
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
struct Point {
|
||||
double x, y;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Point p;
|
||||
Node *left, *right;
|
||||
|
||||
Node(Point _p) : p(_p), left(nullptr), right(nullptr) {}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Utility functions
|
||||
|
||||
```cpp
|
||||
double dist2(const Point& a, const Point& b) {
|
||||
double dx = a.x - b.x;
|
||||
double dy = a.y - b.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Build KD-Tree (2D)
|
||||
|
||||
```cpp
|
||||
Node* build(vector<Point>& pts, int l, int r, int depth) {
|
||||
if (l >= r) return nullptr;
|
||||
|
||||
int dim = depth % 2;
|
||||
int mid = (l + r) / 2;
|
||||
|
||||
nth_element(pts.begin() + l, pts.begin() + mid, pts.begin() + r,
|
||||
[&](const Point& a, const Point& b) {
|
||||
return dim == 0 ? a.x < b.x : a.y < b.y;
|
||||
}
|
||||
);
|
||||
|
||||
Node* node = new Node(pts[mid]);
|
||||
node->left = build(pts, l, mid, depth + 1);
|
||||
node->right = build(pts, mid + 1, r, depth + 1);
|
||||
|
||||
return node;
|
||||
}
|
||||
```
|
||||
|
||||
📌 `nth_element` gives **median in linear time** (better than full sort).
|
||||
|
||||
---
|
||||
|
||||
### Nearest Neighbor Search (2D)
|
||||
|
||||
```cpp
|
||||
void nearest(Node* node, const Point& target, int depth,
|
||||
Point& best, double& bestDist) {
|
||||
if (!node) return;
|
||||
|
||||
double d = dist2(node->p, target);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = node->p;
|
||||
}
|
||||
|
||||
int dim = depth % 2;
|
||||
|
||||
Node *nearChild, *farChild;
|
||||
if ((dim == 0 && target.x < node->p.x) ||
|
||||
(dim == 1 && target.y < node->p.y)) {
|
||||
nearChild = node->left;
|
||||
farChild = node->right;
|
||||
} else {
|
||||
nearChild = node->right;
|
||||
farChild = node->left;
|
||||
}
|
||||
|
||||
nearest(nearChild, target, depth + 1, best, bestDist);
|
||||
|
||||
double diff = (dim == 0)
|
||||
? target.x - node->p.x
|
||||
: target.y - node->p.y;
|
||||
|
||||
if (diff * diff < bestDist) {
|
||||
nearest(farChild, target, depth + 1, best, bestDist);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
💡 **Key idea**:
|
||||
Only search the “other side” if the splitting line is close enough.
|
||||
|
||||
---
|
||||
|
||||
### Usage example
|
||||
|
||||
```cpp
|
||||
int main() {
|
||||
vector<Point> pts = {{2,3}, {5,4}, {9,6}, {4,7}, {8,1}, {7,2}};
|
||||
Node* root = build(pts, 0, pts.size(), 0);
|
||||
|
||||
Point target{9, 2};
|
||||
Point best;
|
||||
double bestDist = 1e18;
|
||||
|
||||
nearest(root, target, 0, best, bestDist);
|
||||
|
||||
cout << best.x << " " << best.y << endl;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 3️⃣ 3D KD-Tree (C++ Template)
|
||||
|
||||
### Changes from 2D:
|
||||
|
||||
* Add `z`
|
||||
* `depth % 3`
|
||||
* Distance formula extends naturally
|
||||
|
||||
---
|
||||
|
||||
### Structures
|
||||
|
||||
```cpp
|
||||
struct Point3 {
|
||||
double x, y, z;
|
||||
};
|
||||
|
||||
struct Node3 {
|
||||
Point3 p;
|
||||
Node3 *left, *right;
|
||||
|
||||
Node3(Point3 _p) : p(_p), left(nullptr), right(nullptr) {}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Distance
|
||||
|
||||
```cpp
|
||||
double dist2(const Point3& a, const Point3& b) {
|
||||
double dx = a.x - b.x;
|
||||
double dy = a.y - b.y;
|
||||
double dz = a.z - b.z;
|
||||
return dx*dx + dy*dy + dz*dz;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Build (3D)
|
||||
|
||||
```cpp
|
||||
Node3* build3(vector<Point3>& pts, int l, int r, int depth) {
|
||||
if (l >= r) return nullptr;
|
||||
|
||||
int dim = depth % 3;
|
||||
int mid = (l + r) / 2;
|
||||
|
||||
nth_element(pts.begin() + l, pts.begin() + mid, pts.begin() + r,
|
||||
[&](const Point3& a, const Point3& b) {
|
||||
if (dim == 0) return a.x < b.x;
|
||||
if (dim == 1) return a.y < b.y;
|
||||
return a.z < b.z;
|
||||
}
|
||||
);
|
||||
|
||||
Node3* node = new Node3(pts[mid]);
|
||||
node->left = build3(pts, l, mid, depth + 1);
|
||||
node->right = build3(pts, mid + 1, r, depth + 1);
|
||||
|
||||
return node;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Nearest Neighbor (3D)
|
||||
|
||||
```cpp
|
||||
void nearest3(Node3* node, const Point3& target, int depth,
|
||||
Point3& best, double& bestDist) {
|
||||
if (!node) return;
|
||||
|
||||
double d = dist2(node->p, target);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = node->p;
|
||||
}
|
||||
|
||||
int dim = depth % 3;
|
||||
|
||||
double diff;
|
||||
Node3 *nearChild, *farChild;
|
||||
|
||||
if ((dim == 0 && target.x < node->p.x) ||
|
||||
(dim == 1 && target.y < node->p.y) ||
|
||||
(dim == 2 && target.z < node->p.z)) {
|
||||
nearChild = node->left;
|
||||
farChild = node->right;
|
||||
} else {
|
||||
nearChild = node->right;
|
||||
farChild = node->left;
|
||||
}
|
||||
|
||||
nearest3(nearChild, target, depth + 1, best, bestDist);
|
||||
|
||||
diff = (dim == 0) ? target.x - node->p.x
|
||||
: (dim == 1) ? target.y - node->p.y
|
||||
: target.z - node->p.z;
|
||||
|
||||
if (diff * diff < bestDist) {
|
||||
nearest3(farChild, target, depth + 1, best, bestDist);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Final mental model (keep this)
|
||||
|
||||
> **KD-Tree = BST where comparison dimension rotates, and pruning is geometric.**
|
||||
|
||||
Once you understand:
|
||||
|
||||
* `depth % k`
|
||||
* median build
|
||||
* “check other side only if needed”
|
||||
|
||||
You’ve mastered KD-Trees.
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
344
Data-Structure/Tree/ScapeGoatTree/Readme.md
Normal file
@@ -0,0 +1,344 @@
|
||||
# Scapegoat Tree (α-Scapegoat Tree)
|
||||
|
||||
## 1. Overview
|
||||
|
||||
A **Scapegoat Tree** is a type of **self-balancing Binary Search Tree (BST)** proposed by *Igal Galperin and Ronald L. Rivest (1993)*.
|
||||
Unlike AVL Trees or Red-Black Trees, it **does not store balance information** (such as heights or colors) inside nodes. Instead, it maintains balance by **occasionally rebuilding subtrees** when imbalance is detected.
|
||||
|
||||
The key idea is:
|
||||
|
||||
> *Allow the tree to become temporarily unbalanced, but detect structural violations and rebuild only when necessary.*
|
||||
|
||||
This makes the Scapegoat Tree conceptually simple while still guaranteeing good asymptotic performance.
|
||||
|
||||
---
|
||||
|
||||
## 2. Balance Criterion
|
||||
|
||||
The tree is parameterized by a real constant:
|
||||
|
||||
[
|
||||
\alpha \in \left(\frac{1}{2}, 1\right)
|
||||
]
|
||||
|
||||
A subtree rooted at node `x` is **α-weight-balanced** if:
|
||||
|
||||
[
|
||||
\max(|\text{left}(x)|, |\text{right}(x)|) \le \alpha \cdot |\text{subtree}(x)|
|
||||
]
|
||||
|
||||
If this condition is violated, `x` is called a **scapegoat**.
|
||||
|
||||
---
|
||||
|
||||
## 3. Height Guarantee
|
||||
|
||||
Let `n` be the number of nodes in the tree.
|
||||
|
||||
The height of a Scapegoat Tree is guaranteed to be:
|
||||
|
||||
[
|
||||
h \le \log_{1/\alpha}(n)
|
||||
]
|
||||
|
||||
Thus:
|
||||
|
||||
* Search: **O(log n)** worst-case
|
||||
* Insert: **O(log n)** amortized
|
||||
* Delete: **O(log n)** amortized
|
||||
|
||||
---
|
||||
|
||||
## 4. Insertion Strategy
|
||||
|
||||
Insertion proceeds in two phases:
|
||||
|
||||
### 4.1 Normal BST Insertion
|
||||
|
||||
* Insert the key as in a standard BST.
|
||||
* Track the depth `d` of the inserted node.
|
||||
|
||||
### 4.2 Violation Detection
|
||||
|
||||
If:
|
||||
[
|
||||
d > \log_{1/\alpha}(n)
|
||||
]
|
||||
then the tree may be unbalanced.
|
||||
|
||||
### 4.3 Scapegoat Identification
|
||||
|
||||
* Traverse upward from the inserted node.
|
||||
* Find the **first ancestor** where the α-balance condition is violated.
|
||||
* This node is the **scapegoat**.
|
||||
|
||||
### 4.4 Rebuilding
|
||||
|
||||
* Rebuild the entire subtree rooted at the scapegoat into a perfectly balanced BST.
|
||||
* This restores global balance.
|
||||
|
||||
---
|
||||
|
||||
## 5. Deletion Strategy
|
||||
|
||||
Deletion is handled lazily:
|
||||
|
||||
1. Perform standard BST deletion.
|
||||
2. Maintain:
|
||||
|
||||
* `n`: current node count
|
||||
* `max_n`: maximum node count ever reached
|
||||
3. If:
|
||||
[
|
||||
n < \alpha \cdot \text{max_n}
|
||||
]
|
||||
then:
|
||||
|
||||
* Rebuild the **entire tree**
|
||||
* Set `max_n = n`
|
||||
|
||||
This avoids frequent rebalancing during small deletions.
|
||||
|
||||
---
|
||||
|
||||
## 6. Rebuilding a Subtree
|
||||
|
||||
Rebuilding consists of two steps:
|
||||
|
||||
1. **Flatten**
|
||||
|
||||
* Perform an inorder traversal of the subtree
|
||||
* Store nodes in a sorted array
|
||||
|
||||
2. **Rebuild**
|
||||
|
||||
* Recursively construct a perfectly balanced BST from the array
|
||||
|
||||
This operation takes **O(k)** time for a subtree of size `k`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Comparison with Other BSTs
|
||||
|
||||
| Tree Type | Balance Info | Rebalancing | Worst-case Height |
|
||||
| ------------- | ------------ | --------------- | ------------------ |
|
||||
| AVL | Height | Rotations | O(log n) |
|
||||
| Red-Black | Color | Rotations | O(log n) |
|
||||
| Splay | None | Access-based | Amortized O(log n) |
|
||||
| **Scapegoat** | None | Subtree rebuild | O(log n) |
|
||||
|
||||
---
|
||||
|
||||
## 8. Advantages and Disadvantages
|
||||
|
||||
### Advantages
|
||||
|
||||
* Simple node structure
|
||||
* No rotations
|
||||
* Deterministic height bound
|
||||
* Easy to implement correctly
|
||||
|
||||
### Disadvantages
|
||||
|
||||
* Rebuild cost can be high
|
||||
* Worse constant factors than AVL/RB trees
|
||||
* Not ideal for real-time systems
|
||||
|
||||
---
|
||||
|
||||
## 9. Typical Use Cases
|
||||
|
||||
* Educational purposes
|
||||
* Systems where simplicity > constant factors
|
||||
* Situations where rotations are undesirable
|
||||
* Batch-heavy insert/delete workloads
|
||||
|
||||
---
|
||||
|
||||
# C++ Template: Scapegoat Tree
|
||||
|
||||
Below is a **minimal, clean, academic-style template**, suitable for learning and extension.
|
||||
|
||||
---
|
||||
|
||||
## 1. Node Definition
|
||||
|
||||
```cpp
|
||||
struct Node {
|
||||
int key;
|
||||
Node *left, *right, *parent;
|
||||
int size;
|
||||
|
||||
Node(int k)
|
||||
: key(k), left(nullptr), right(nullptr), parent(nullptr), size(1) {}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Core Class Skeleton
|
||||
|
||||
```cpp
|
||||
class ScapegoatTree {
|
||||
private:
|
||||
const double alpha = 0.75;
|
||||
|
||||
Node* root = nullptr;
|
||||
int n = 0;
|
||||
int max_n = 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Utility Functions
|
||||
|
||||
### 3.1 Subtree Size Maintenance
|
||||
|
||||
```cpp
|
||||
int getSize(Node* x) {
|
||||
return x ? x->size : 0;
|
||||
}
|
||||
|
||||
void update(Node* x) {
|
||||
if (x) {
|
||||
x->size = getSize(x->left) + getSize(x->right) + 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Inorder Flatten
|
||||
|
||||
```cpp
|
||||
void flatten(Node* x, std::vector<Node*>& arr) {
|
||||
if (!x) return;
|
||||
flatten(x->left, arr);
|
||||
arr.push_back(x);
|
||||
flatten(x->right, arr);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Build Balanced Tree
|
||||
|
||||
```cpp
|
||||
Node* build(std::vector<Node*>& arr, int l, int r, Node* parent) {
|
||||
if (l > r) return nullptr;
|
||||
int m = (l + r) / 2;
|
||||
|
||||
Node* x = arr[m];
|
||||
x->parent = parent;
|
||||
x->left = build(arr, l, m - 1, x);
|
||||
x->right = build(arr, m + 1, r, x);
|
||||
|
||||
update(x);
|
||||
return x;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Rebuild Subtree
|
||||
|
||||
```cpp
|
||||
void rebuild(Node* x) {
|
||||
std::vector<Node*> nodes;
|
||||
flatten(x, nodes);
|
||||
|
||||
Node* parent = x->parent;
|
||||
Node* newSub = build(nodes, 0, nodes.size() - 1, parent);
|
||||
|
||||
if (!parent) {
|
||||
root = newSub;
|
||||
} else if (parent->left == x) {
|
||||
parent->left = newSub;
|
||||
} else {
|
||||
parent->right = newSub;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Balance Check
|
||||
|
||||
```cpp
|
||||
bool isBalanced(Node* x) {
|
||||
return getSize(x->left) <= alpha * x->size &&
|
||||
getSize(x->right) <= alpha * x->size;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Insertion
|
||||
|
||||
```cpp
|
||||
void insert(int key) {
|
||||
if (!root) {
|
||||
root = new Node(key);
|
||||
n = max_n = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
Node* cur = root;
|
||||
Node* parent = nullptr;
|
||||
int depth = 0;
|
||||
|
||||
while (cur) {
|
||||
parent = cur;
|
||||
cur->size++;
|
||||
if (key < cur->key)
|
||||
cur = cur->left;
|
||||
else
|
||||
cur = cur->right;
|
||||
depth++;
|
||||
}
|
||||
|
||||
Node* x = new Node(key);
|
||||
x->parent = parent;
|
||||
|
||||
if (key < parent->key)
|
||||
parent->left = x;
|
||||
else
|
||||
parent->right = x;
|
||||
|
||||
n++;
|
||||
max_n = std::max(max_n, n);
|
||||
|
||||
if (depth > std::log(n) / std::log(1.0 / alpha)) {
|
||||
Node* y = x->parent;
|
||||
while (y && isBalanced(y)) {
|
||||
y = y->parent;
|
||||
}
|
||||
if (y) rebuild(y);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Search (Standard BST)
|
||||
|
||||
```cpp
|
||||
Node* find(int key) {
|
||||
Node* cur = root;
|
||||
while (cur) {
|
||||
if (key == cur->key) return cur;
|
||||
if (key < cur->key) cur = cur->left;
|
||||
else cur = cur->right;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Notes for Extension
|
||||
|
||||
* Add deletion with global rebuild
|
||||
* Support order-statistics (`k`-th element)
|
||||
* Replace `int key` with templates
|
||||
* Remove parent pointers using recursion
|
||||
132
Data-Structure/Tree/ScapeGoatTree/template.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
int key;
|
||||
Node *left, *right, *parent;
|
||||
int size;
|
||||
|
||||
Node(int k)
|
||||
: key(k), left(nullptr), right(nullptr), parent(nullptr), size(1) {}
|
||||
};
|
||||
|
||||
class ScapegoatTree {
|
||||
private:
|
||||
const double alpha = 0.75;
|
||||
Node* root = nullptr;
|
||||
int n = 0; // current node count
|
||||
int max_n = 0; // historical maximum node count
|
||||
|
||||
int getSize(Node* x) {
|
||||
return x ? x->size : 0;
|
||||
}
|
||||
|
||||
void update(Node* x) {
|
||||
if (x) x->size = getSize(x->left) + getSize(x->right) + 1;
|
||||
}
|
||||
|
||||
void flatten(Node* x, vector<Node*>& arr) {
|
||||
if (!x) return;
|
||||
flatten(x->left, arr);
|
||||
arr.push_back(x);
|
||||
flatten(x->right, arr);
|
||||
}
|
||||
|
||||
Node* build(vector<Node*>& arr, int l, int r, Node* parent) {
|
||||
if (l > r) return nullptr;
|
||||
int m = (l + r) / 2;
|
||||
Node* x = arr[m];
|
||||
x->parent = parent;
|
||||
x->left = build(arr, l, m - 1, x);
|
||||
x->right = build(arr, m + 1, r, x);
|
||||
update(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
void rebuild(Node* x) {
|
||||
vector<Node*> nodes;
|
||||
flatten(x, nodes);
|
||||
Node* parent = x->parent;
|
||||
Node* newSub = build(nodes, 0, nodes.size() - 1, parent);
|
||||
|
||||
if (!parent) root = newSub;
|
||||
else if (parent->left == x) parent->left = newSub;
|
||||
else parent->right = newSub;
|
||||
}
|
||||
|
||||
bool isBalanced(Node* x) {
|
||||
return getSize(x->left) <= alpha * x->size &&
|
||||
getSize(x->right) <= alpha * x->size;
|
||||
}
|
||||
|
||||
public:
|
||||
void insert(int key) {
|
||||
if (!root) {
|
||||
root = new Node(key);
|
||||
n = max_n = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
Node* cur = root;
|
||||
Node* parent = nullptr;
|
||||
int depth = 0;
|
||||
|
||||
while (cur) {
|
||||
parent = cur;
|
||||
cur->size++;
|
||||
if (key < cur->key) cur = cur->left;
|
||||
else cur = cur->right;
|
||||
depth++;
|
||||
}
|
||||
|
||||
Node* x = new Node(key);
|
||||
x->parent = parent;
|
||||
if (key < parent->key) parent->left = x;
|
||||
else parent->right = x;
|
||||
|
||||
n++;
|
||||
max_n = max(max_n, n);
|
||||
|
||||
if (depth > log(n) / log(1.0 / alpha)) {
|
||||
Node* y = x->parent;
|
||||
while (y && isBalanced(y)) y = y->parent;
|
||||
if (y) rebuild(y);
|
||||
}
|
||||
}
|
||||
|
||||
Node* find(int key) {
|
||||
Node* cur = root;
|
||||
while (cur) {
|
||||
if (key == cur->key) return cur;
|
||||
if (key < cur->key) cur = cur->left;
|
||||
else cur = cur->right;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void inorder(Node* x) {
|
||||
if (!x) return;
|
||||
inorder(x->left);
|
||||
cout << x->key << " ";
|
||||
inorder(x->right);
|
||||
}
|
||||
|
||||
void print() { inorder(root); cout << endl; }
|
||||
};
|
||||
|
||||
int main() {
|
||||
ScapegoatTree tree;
|
||||
|
||||
vector<int> keys = {50, 20, 70, 10, 30, 60, 80, 25};
|
||||
for (int k : keys) tree.insert(k);
|
||||
|
||||
cout << "Inorder traversal of Scapegoat Tree: ";
|
||||
tree.print();
|
||||
|
||||
int query = 25;
|
||||
Node* res = tree.find(query);
|
||||
if (res) cout << "Found " << query << " in tree.\n";
|
||||
else cout << query << " not found.\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
72
Data-Structure/Tree/SegmentTree/template.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
const int MAXN = 100005;
|
||||
long long tree[MAXN << 2];
|
||||
long long lazy[MAXN << 2];
|
||||
int a[MAXN];
|
||||
|
||||
//Renew ancestor node
|
||||
void pushUp(int p){
|
||||
//Use *sum* as an example
|
||||
tree[p] = tree[p << 1] + tree[p << 1 | 1];
|
||||
}
|
||||
|
||||
//Renew children through LazyTag(DirtyBit)
|
||||
void pushDown(int p, int l, int r){
|
||||
if(lazy[p] != 0){
|
||||
int mid = l + (r-l)/2;
|
||||
//Change tree and array
|
||||
//if lazy[p]!0, pass lazy to lazy[kids](*length), tree[kids] + lazy, lazy[p]=0
|
||||
lazy[p << 1] += lazy[p];
|
||||
tree[p << 1] += lazy[p] * (mid-l+1);
|
||||
lazy[p << 1 | 1] += lazy[p];
|
||||
tree[p << 1 | 1] += lazy[p] * (r-mid);
|
||||
/*You *Should Not* Recursively pushDown
|
||||
Controlled by void update() or void ll query()
|
||||
Otherwise: *O(log n) -> O(n)*
|
||||
*/
|
||||
lazy[p] = 0;
|
||||
}
|
||||
}
|
||||
void build(int p, int l, int r){
|
||||
lazy[p] = 0;
|
||||
if(l == r) {
|
||||
tree[p] = a[l];
|
||||
return;
|
||||
//single node noneed to pushup
|
||||
}
|
||||
int mid = l + (r-l)/2;
|
||||
build(p << 1, l, mid);
|
||||
build(p << 1 | 1, mid+1, r);
|
||||
pushUp(p);
|
||||
}
|
||||
// Update: In [L, R], each add v
|
||||
void update(int L, int R, int v, int l, int r, int p){
|
||||
//Fully covered by [l, r]
|
||||
if (L <= l && r <= R) {
|
||||
tree[p] += (long long)v * (r-l+1);
|
||||
lazy[p] += v;
|
||||
return;
|
||||
}
|
||||
//Unfully covered by [l, r]
|
||||
pushDown(p, l, r);
|
||||
int mid = l + (r-l)/2;
|
||||
//Now Recursively Update
|
||||
if (L <= mid) update(L, R, v, l, mid, p << 1);
|
||||
if (mid > R) update(L, R, v, mid+1, r, p << 1 | 1);
|
||||
pushUp(p);
|
||||
}
|
||||
long long query(int L, int R, int l, int r, int p){
|
||||
if (L <= l && r <= R) return tree[p];
|
||||
pushDown(p, l, r);
|
||||
int mid = (l+r) >> 1;
|
||||
long long res = 0;
|
||||
if(L <= mid) res += query(L, R, l, mid, p << 1);
|
||||
if(R > mid) res += query(L, R, mid+1, r, p << 1 | 1);
|
||||
return res;
|
||||
}
|
||||
int main(){
|
||||
int n;
|
||||
return 0;
|
||||
}
|
||||
199
Data-Structure/Tree/SplayTree/Readme.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 🌳 Splay Tree — A Self-Adjusting Binary Search Tree
|
||||
|
||||
## 1. What is a Splay Tree?
|
||||
|
||||
A **Splay Tree** is a type of **self-adjusting Binary Search Tree (BST)**.
|
||||
Unlike AVL or Red–Black Trees, it **does not maintain explicit balance rules**.
|
||||
|
||||
Instead, it follows one simple idea:
|
||||
|
||||
> **Every time a node is accessed, move it to the root using rotations.**
|
||||
|
||||
This operation is called **splaying**.
|
||||
|
||||
As a result, the tree dynamically reorganizes itself based on **access patterns**, not height constraints.
|
||||
|
||||
---
|
||||
|
||||
## 2. Core Idea: “Access → Root”
|
||||
|
||||
In a Splay Tree:
|
||||
|
||||
* Searching for a key
|
||||
* Inserting a node
|
||||
* Deleting a node
|
||||
|
||||
All end with the same operation:
|
||||
|
||||
> 🔁 **Splay the accessed node to the root**
|
||||
|
||||
This is done via a sequence of rotations:
|
||||
|
||||
* **Zig** (single rotation)
|
||||
* **Zig–Zig** (double rotation, same direction)
|
||||
* **Zig–Zag** (double rotation, opposite directions)
|
||||
|
||||
After splaying:
|
||||
|
||||
* The accessed node becomes the **root**
|
||||
* Nodes “related” to it move closer to the top
|
||||
* Frequently accessed nodes stay shallow
|
||||
|
||||
---
|
||||
|
||||
## 3. What Makes Splay Trees Special?
|
||||
|
||||
### 3.1 No Explicit Balance Condition
|
||||
|
||||
Splay Trees:
|
||||
|
||||
* Do **not** store height, color, or priority
|
||||
* Do **not** rebalance on every insert/delete
|
||||
* May look “unbalanced” at any moment
|
||||
|
||||
Yet, surprisingly:
|
||||
|
||||
> ✅ **All operations run in amortized (O(\log n)) time**
|
||||
|
||||
This is proven using amortized analysis (potential method).
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Self-Adjusting Behavior
|
||||
|
||||
Splay Trees adapt automatically to access patterns:
|
||||
|
||||
* Recently accessed nodes become fast to access again
|
||||
* Sequential access becomes extremely efficient
|
||||
* “Hot” keys naturally move near the root
|
||||
|
||||
This gives Splay Trees several strong properties:
|
||||
|
||||
* **Working-set property**
|
||||
* **Static optimality**
|
||||
* **Dynamic finger property**
|
||||
|
||||
These properties are hard (or impossible) to guarantee with strictly balanced trees.
|
||||
|
||||
---
|
||||
|
||||
## 4. Splay Tree as an “Easy-to-Pivot” Tree
|
||||
|
||||
A very useful way to think about Splay Trees is:
|
||||
|
||||
> 🧠 **Any accessed key can instantly become a pivot of the entire tree**
|
||||
|
||||
Once a node `x` is splayed to the root:
|
||||
|
||||
* All keys `< x` are in the left subtree
|
||||
* All keys `> x` are in the right subtree
|
||||
|
||||
This leads directly to powerful structural operations.
|
||||
|
||||
---
|
||||
|
||||
## 5. Split and Merge — The Real Power
|
||||
|
||||
### 5.1 Split Operation
|
||||
|
||||
**Split** divides a tree into two trees based on a key `k`:
|
||||
|
||||
* `Left`: all keys `≤ k`
|
||||
* `Right`: all keys `> k`
|
||||
|
||||
In a Splay Tree, this is easy:
|
||||
|
||||
1. Search for `k` (or the closest node)
|
||||
2. Splay it to the root
|
||||
3. Detach its left or right subtree
|
||||
|
||||
Because access automatically moves nodes to the root, **split is almost free**.
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Merge Operation
|
||||
|
||||
**Merge** combines two trees:
|
||||
|
||||
* All keys in `Left` are smaller than those in `Right`
|
||||
|
||||
Method:
|
||||
|
||||
1. Splay the **maximum node** of `Left` to the root
|
||||
2. Attach `Right` as its right child
|
||||
|
||||
Again, splaying makes this simple and efficient.
|
||||
|
||||
---
|
||||
|
||||
## 6. Interval and Range Manipulation
|
||||
|
||||
By combining **split** and **merge**, Splay Trees can easily isolate any interval `[L, R]`:
|
||||
|
||||
```text
|
||||
T
|
||||
├─ split by R → A , C
|
||||
└─ split A by L-1 → B , Mid
|
||||
```
|
||||
|
||||
Now:
|
||||
|
||||
* `Mid` contains exactly the range `[L, R]`
|
||||
* You can apply operations to `Mid` only
|
||||
* Then merge everything back
|
||||
|
||||
This makes Splay Trees ideal for:
|
||||
|
||||
* Sequence manipulation
|
||||
* Range updates
|
||||
* Subarray operations
|
||||
|
||||
---
|
||||
|
||||
## 7. Typical Applications
|
||||
|
||||
### 7.1 Text Editors and Ropes
|
||||
|
||||
* Cursor moves locally
|
||||
* Recent edits are reused
|
||||
* Cut / paste / reverse ranges efficiently
|
||||
|
||||
### 7.2 Dynamic Trees (Link–Cut Tree)
|
||||
|
||||
* Splay Tree is the **core data structure**
|
||||
* Supports path queries and updates
|
||||
* AVL / Red–Black Trees cannot replace it here
|
||||
|
||||
### 7.3 Cache-like Access Patterns
|
||||
|
||||
* Frequently accessed keys stay near the root
|
||||
* No need to maintain explicit frequency counters
|
||||
|
||||
---
|
||||
|
||||
## 8. Comparison with Other BSTs
|
||||
|
||||
| Tree Type | Balance Method | Worst Case | Interval Ops | Adaptivity |
|
||||
| --------- | --------------- | ---------------------- | ------------ | ---------- |
|
||||
| AVL | Height | (O(\log n)) | Hard | ❌ |
|
||||
| Red–Black | Color rules | (O(\log n)) | Hard | ❌ |
|
||||
| Treap | Random priority | (O(\log n)) (expected) | Easy | ❌ |
|
||||
| **Splay** | Access-based | (O(n)) (single op) | **Easy** | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 9. When NOT to Use Splay Trees
|
||||
|
||||
Splay Trees are **not ideal** when:
|
||||
|
||||
* Strict worst-case latency is required
|
||||
* Access patterns are uniformly random
|
||||
* Predictable structure is more important than adaptivity
|
||||
|
||||
In such cases, AVL or Red–Black Trees may be better.
|
||||
|
||||
---
|
||||
|
||||
## 10. One-Sentence Summary
|
||||
|
||||
> **A Splay Tree is a self-adjusting BST where any accessed node becomes the root, making the tree extremely easy to split, merge, and reorganize around chosen keys — with strong amortized performance guarantees.**
|
||||
125
Data-Structure/Tree/SplayTree/template.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
int key;
|
||||
Node *fa, *ch[2];
|
||||
int sz;
|
||||
|
||||
Node(int k) : key(k), fa(nullptr), sz(1) {
|
||||
ch[0] = ch[1] = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
inline int size(Node* x) {
|
||||
return x ? x->sz : 0;
|
||||
}
|
||||
|
||||
inline void pull(Node* x) {
|
||||
if (x)
|
||||
x->sz = 1 + size(x->ch[0]) + size(x->ch[1]);
|
||||
}
|
||||
|
||||
inline bool is_right(Node* x) {
|
||||
return x->fa && x->fa->ch[1] == x;
|
||||
}
|
||||
|
||||
void rotate(Node* x) {
|
||||
Node* p = x->fa;
|
||||
Node* g = p->fa;
|
||||
bool dir = (x == p->ch[1]); // 0 = left, 1 = right
|
||||
|
||||
// connect x's opposite child to p
|
||||
p->ch[dir] = x->ch[dir ^ 1];
|
||||
if (x->ch[dir ^ 1])
|
||||
x->ch[dir ^ 1]->fa = p;
|
||||
|
||||
// move p under x
|
||||
x->ch[dir ^ 1] = p;
|
||||
p->fa = x;
|
||||
|
||||
// connect x to g
|
||||
x->fa = g;
|
||||
if (g) {
|
||||
if (g->ch[0] == p) g->ch[0] = x;
|
||||
else g->ch[1] = x;
|
||||
}
|
||||
|
||||
pull(p);
|
||||
pull(x);
|
||||
}
|
||||
|
||||
void splay(Node* x, Node* goal = nullptr) {
|
||||
while (x->fa != goal) {
|
||||
Node* p = x->fa;
|
||||
Node* g = p->fa;
|
||||
|
||||
if (g != goal) {
|
||||
if ((g->ch[0] == p) == (p->ch[0] == x))
|
||||
rotate(p); // zig-zig
|
||||
else
|
||||
rotate(x); // zig-zag
|
||||
}
|
||||
rotate(x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Split by k, result: left <= k, right >= k
|
||||
*/
|
||||
void split(Node* root, int k, Node*& left, Node*& right) {
|
||||
Node* cur = root;
|
||||
Node* last = nullptr;
|
||||
|
||||
while (cur) {
|
||||
last = cur;
|
||||
if (k < cur->key)
|
||||
cur = cur->ch[0];
|
||||
else
|
||||
cur = cur->ch[1];
|
||||
}
|
||||
|
||||
if (last)
|
||||
splay(last);
|
||||
|
||||
if (!last) {
|
||||
left = right = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (last->key <= k) {
|
||||
left = last;
|
||||
right = last->ch[1];
|
||||
if (right) right->fa = nullptr;
|
||||
left->ch[1] = nullptr;
|
||||
pull(left);
|
||||
} else {
|
||||
right = last;
|
||||
left = last->ch[0];
|
||||
if (left) left->fa = nullptr;
|
||||
right->ch[0] = nullptr;
|
||||
pull(right);
|
||||
}
|
||||
}
|
||||
|
||||
Node* merge(Node* left, Node* right) {
|
||||
if (!left) return right;
|
||||
if (!right) return left;
|
||||
|
||||
// find max of left
|
||||
Node* cur = left;
|
||||
while (cur->ch[1])
|
||||
cur = cur->ch[1];
|
||||
|
||||
splay(cur); // max becomes root
|
||||
cur->ch[1] = right;
|
||||
right->fa = cur;
|
||||
|
||||
pull(cur);
|
||||
return cur;
|
||||
}
|
||||
|
||||
int main(){
|
||||
return 0;
|
||||
}
|
||||
67
Data-Structure/Tree/Treap/FHQ.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
int key, priority;
|
||||
int left, right;
|
||||
int size;
|
||||
};
|
||||
|
||||
const int MAXN = 200000;
|
||||
Node tr[MAXN];
|
||||
int tot = 0;
|
||||
|
||||
int newNode(int key) {
|
||||
++tot;
|
||||
tr[tot] = {key, rand(), 0, 0, 1};
|
||||
return tot;
|
||||
}
|
||||
|
||||
int getSize(int x) {
|
||||
return x ? tr[x].size : 0;
|
||||
}
|
||||
|
||||
void pushUp(int x) {
|
||||
tr[x].size = getSize(tr[x].left) + getSize(tr[x].right) + 1;
|
||||
}
|
||||
|
||||
// Split by key: left <= key, right > key
|
||||
void split(int root, int key, int &x, int &y) {
|
||||
if (!root) {
|
||||
x = y = 0;
|
||||
return;
|
||||
}
|
||||
if (tr[root].key <= key) {
|
||||
x = root;
|
||||
split(tr[root].right, key, tr[root].right, y);
|
||||
pushUp(x);
|
||||
} else {
|
||||
y = root;
|
||||
split(tr[root].left, key, x, tr[root].left);
|
||||
pushUp(y);
|
||||
}
|
||||
}
|
||||
|
||||
int merge(int x, int y) {
|
||||
if (!x || !y) return x | y;
|
||||
if (tr[x].priority > tr[y].priority) {
|
||||
tr[x].right = merge(tr[x].right, y);
|
||||
pushUp(x);
|
||||
return x;
|
||||
} else {
|
||||
tr[y].left = merge(x, tr[y].left);
|
||||
pushUp(y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert key into treap
|
||||
void insert(int &root, int key) {
|
||||
int x, y;
|
||||
split(root, key, x, y);
|
||||
root = merge(merge(x, newNode(key)), y);
|
||||
}
|
||||
|
||||
int main(){
|
||||
return 0;
|
||||
}
|
||||
283
Data-Structure/Tree/Treap/Readme.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# Treap: A Randomized Balanced Binary Search Tree
|
||||
|
||||
## 1. Overview
|
||||
|
||||
A **Treap** (Tree + Heap) is a **randomized balanced binary search tree** that simultaneously satisfies:
|
||||
|
||||
1. **Binary Search Tree (BST) property** on keys
|
||||
2. **Heap property** on priorities
|
||||
|
||||
By assigning each node a random priority, Treap achieves **expected logarithmic height**, providing efficient dynamic set and sequence operations.
|
||||
|
||||
Treaps are widely used in:
|
||||
|
||||
* Dynamic ordered sets
|
||||
* Sequence maintenance
|
||||
* Range query problems
|
||||
* Competitive programming as a flexible alternative to AVL / Red-Black trees
|
||||
|
||||
---
|
||||
|
||||
## 2. Structural Properties
|
||||
|
||||
Each Treap node contains:
|
||||
|
||||
* `key`: used to maintain BST ordering
|
||||
* `priority`: a randomly assigned value
|
||||
* `left`, `right`: child pointers (or indices)
|
||||
* Optional augmented data (e.g., subtree size, sum)
|
||||
|
||||
### 2.1 BST Property
|
||||
|
||||
For any node `u`:
|
||||
|
||||
* All keys in `u.left` are **less than** `u.key`
|
||||
* All keys in `u.right` are **greater than** `u.key`
|
||||
|
||||
### 2.2 Heap Property
|
||||
|
||||
For any node `u`:
|
||||
|
||||
* `priority(u)` is **greater than or equal to** the priorities of its children
|
||||
(Max-heap convention; min-heap also works symmetrically)
|
||||
|
||||
---
|
||||
|
||||
## 3. Why Randomization Works
|
||||
|
||||
If priorities are independent random variables, then:
|
||||
|
||||
* The expected height of the Treap is **O(log n)**
|
||||
* All standard BST operations run in **expected O(log n)** time
|
||||
|
||||
This avoids the need for strict rebalancing rules, unlike AVL or Red-Black trees.
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementation Models
|
||||
|
||||
In practice, Treaps are implemented as **pointer-based trees**.
|
||||
However, for efficiency and memory safety, most implementations use:
|
||||
|
||||
> **Array-based node pools with integer indices simulating pointers**
|
||||
|
||||
This approach:
|
||||
|
||||
* Avoids frequent dynamic memory allocation
|
||||
* Improves cache locality
|
||||
* Is standard in algorithmic contexts
|
||||
|
||||
---
|
||||
|
||||
## 5. Rotating Treap (Classic Treap)
|
||||
|
||||
### 5.1 Core Idea
|
||||
|
||||
Insertion proceeds as in a normal BST by `key`.
|
||||
If the heap property is violated after insertion, **tree rotations** are used to restore it.
|
||||
|
||||
### 5.2 Operations
|
||||
|
||||
* Insert: BST insert + rotations
|
||||
* Delete: rotate node down until removable
|
||||
* Search: standard BST search
|
||||
|
||||
### 5.3 Characteristics
|
||||
|
||||
**Advantages**
|
||||
|
||||
* Conceptually close to AVL / Red-Black trees
|
||||
* Intuitive if rotations are already familiar
|
||||
|
||||
**Disadvantages**
|
||||
|
||||
* Rotation logic can be error-prone
|
||||
* Slightly harder to extend for sequence problems
|
||||
|
||||
---
|
||||
|
||||
## 6. FHQ Treap (Split & Merge Treap)
|
||||
|
||||
### 6.1 Core Idea
|
||||
|
||||
The FHQ Treap (named after its proposer) eliminates rotations entirely.
|
||||
|
||||
It relies on two fundamental operations:
|
||||
|
||||
1. **Split**
|
||||
|
||||
* Divide a Treap into two based on a key (or position)
|
||||
2. **Merge**
|
||||
|
||||
* Combine two Treaps assuming all keys in the left are smaller
|
||||
|
||||
All operations are expressed using **split + merge**, preserving both BST and heap properties automatically.
|
||||
|
||||
---
|
||||
|
||||
### 6.2 Why FHQ Treap Is Popular
|
||||
|
||||
* No explicit rotations
|
||||
* Cleaner and more modular code
|
||||
* Ideal for **implicit Treap** (sequence problems)
|
||||
* Easier to augment with lazy propagation
|
||||
|
||||
As a result, FHQ Treap is the **dominant Treap variant** in competitive programming.
|
||||
|
||||
---
|
||||
|
||||
## 7. Comparison Summary
|
||||
|
||||
| Aspect | Rotating Treap | FHQ Treap |
|
||||
| ---------------- | -------------- | ------------- |
|
||||
| Balancing method | Rotations | Split & Merge |
|
||||
| Code complexity | Medium | Low |
|
||||
| Extensibility | Moderate | Excellent |
|
||||
| Sequence support | Harder | Natural |
|
||||
| Contest usage | Less common | Very common |
|
||||
|
||||
---
|
||||
|
||||
## 8. Rotating Treap — C++ Template
|
||||
|
||||
```cpp
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
int key, priority;
|
||||
int left, right;
|
||||
int size;
|
||||
};
|
||||
|
||||
const int MAXN = 200000;
|
||||
Node tr[MAXN];
|
||||
int tot = 0;
|
||||
int root = 0;
|
||||
|
||||
int newNode(int key) {
|
||||
++tot;
|
||||
tr[tot] = {key, rand(), 0, 0, 1};
|
||||
return tot;
|
||||
}
|
||||
|
||||
int getSize(int x) {
|
||||
return x ? tr[x].size : 0;
|
||||
}
|
||||
|
||||
void pushUp(int x) {
|
||||
tr[x].size = getSize(tr[x].left) + getSize(tr[x].right) + 1;
|
||||
}
|
||||
|
||||
void rotateLeft(int &x) {
|
||||
int y = tr[x].right;
|
||||
tr[x].right = tr[y].left;
|
||||
tr[y].left = x;
|
||||
pushUp(x);
|
||||
pushUp(y);
|
||||
x = y;
|
||||
}
|
||||
|
||||
void rotateRight(int &x) {
|
||||
int y = tr[x].left;
|
||||
tr[x].left = tr[y].right;
|
||||
tr[y].right = x;
|
||||
pushUp(x);
|
||||
pushUp(y);
|
||||
x = y;
|
||||
}
|
||||
|
||||
void insert(int &x, int key) {
|
||||
if (!x) {
|
||||
x = newNode(key);
|
||||
return;
|
||||
}
|
||||
if (key < tr[x].key) {
|
||||
insert(tr[x].left, key);
|
||||
if (tr[tr[x].left].priority > tr[x].priority)
|
||||
rotateRight(x);
|
||||
} else {
|
||||
insert(tr[x].right, key);
|
||||
if (tr[tr[x].right].priority > tr[x].priority)
|
||||
rotateLeft(x);
|
||||
}
|
||||
pushUp(x);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. FHQ Treap — C++ Template
|
||||
|
||||
```cpp
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
int key, priority;
|
||||
int left, right;
|
||||
int size;
|
||||
};
|
||||
|
||||
const int MAXN = 200000;
|
||||
Node tr[MAXN];
|
||||
int tot = 0;
|
||||
|
||||
int newNode(int key) {
|
||||
++tot;
|
||||
tr[tot] = {key, rand(), 0, 0, 1};
|
||||
return tot;
|
||||
}
|
||||
|
||||
int getSize(int x) {
|
||||
return x ? tr[x].size : 0;
|
||||
}
|
||||
|
||||
void pushUp(int x) {
|
||||
tr[x].size = getSize(tr[x].left) + getSize(tr[x].right) + 1;
|
||||
}
|
||||
|
||||
// Split by key: left <= key, right > key
|
||||
void split(int root, int key, int &x, int &y) {
|
||||
if (!root) {
|
||||
x = y = 0;
|
||||
return;
|
||||
}
|
||||
if (tr[root].key <= key) {
|
||||
x = root;
|
||||
split(tr[root].right, key, tr[root].right, y);
|
||||
pushUp(x);
|
||||
} else {
|
||||
y = root;
|
||||
split(tr[root].left, key, x, tr[root].left);
|
||||
pushUp(y);
|
||||
}
|
||||
}
|
||||
|
||||
int merge(int x, int y) {
|
||||
if (!x || !y) return x | y;
|
||||
if (tr[x].priority > tr[y].priority) {
|
||||
tr[x].right = merge(tr[x].right, y);
|
||||
pushUp(x);
|
||||
return x;
|
||||
} else {
|
||||
tr[y].left = merge(x, tr[y].left);
|
||||
pushUp(y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert key into treap
|
||||
void insert(int &root, int key) {
|
||||
int x, y;
|
||||
split(root, key, x, y);
|
||||
root = merge(merge(x, newNode(key)), y);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Closing Remarks
|
||||
|
||||
Treap combines the simplicity of BSTs with the robustness of randomized balancing.
|
||||
Among its variants, **FHQ Treap** stands out for its elegance, extensibility, and practical usefulness, especially in sequence-based problems.
|
||||
67
Data-Structure/Tree/Treap/rotate.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
struct Node {
|
||||
int key, priority;
|
||||
int left, right;
|
||||
int size;
|
||||
};
|
||||
|
||||
const int MAXN = 200000;
|
||||
Node tr[MAXN];
|
||||
int tot = 0;
|
||||
int root = 0;
|
||||
|
||||
int newNode(int key) {
|
||||
++tot;
|
||||
tr[tot] = {key, rand(), 0, 0, 1};
|
||||
return tot;
|
||||
}
|
||||
|
||||
int getSize(int x) {
|
||||
return x ? tr[x].size : 0;
|
||||
}
|
||||
|
||||
void pushUp(int x) {
|
||||
tr[x].size = getSize(tr[x].left) + getSize(tr[x].right) + 1;
|
||||
}
|
||||
|
||||
void rotateLeft(int &x) {
|
||||
int y = tr[x].right;
|
||||
tr[x].right = tr[y].left;
|
||||
tr[y].left = x;
|
||||
pushUp(x);
|
||||
pushUp(y);
|
||||
x = y;
|
||||
}
|
||||
|
||||
void rotateRight(int &x) {
|
||||
int y = tr[x].left;
|
||||
tr[x].left = tr[y].right;
|
||||
tr[y].right = x;
|
||||
pushUp(x);
|
||||
pushUp(y);
|
||||
x = y;
|
||||
}
|
||||
|
||||
void insert(int &x, int key) {
|
||||
if (!x) {
|
||||
x = newNode(key);
|
||||
return;
|
||||
}
|
||||
if (key < tr[x].key) {
|
||||
insert(tr[x].left, key);
|
||||
if (tr[tr[x].left].priority > tr[x].priority)
|
||||
rotateRight(x);
|
||||
} else {
|
||||
insert(tr[x].right, key);
|
||||
if (tr[tr[x].right].priority > tr[x].priority)
|
||||
rotateLeft(x);
|
||||
}
|
||||
pushUp(x);
|
||||
}
|
||||
|
||||
int main(){
|
||||
|
||||
return 0;
|
||||
}
|
||||