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;
|
||||||
|
}
|
||||||