feat: add generic iterative segment tree class

This commit is contained in:
2026-02-09 23:26:16 +00:00
parent b95431499f
commit a5921bdcf9
6 changed files with 583 additions and 1 deletions
+1
View File
@@ -57,3 +57,4 @@ Thumbs.db
# binary
sub_notifs
iter_seg_tree
+8
View File
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 4.0)
project(iter_seg_tree)
set(CMAKE_CXX_STANDARD 20)
add_executable(iter_seg_tree src/main.cpp)
target_include_directories(iter_seg_tree PRIVATE ${CMAKE_SOURCE_DIR}/include)
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include <functional>
#include <vector>
#define LEFT(i) (i << 1)
#define RIGHT(i) ((i << 1) | 1)
#define IS_RIGHT_CHILD(i) ((i) & 1)
template <typename T>
class IterSegTree {
private:
int len_;
std::vector<T> seg_tree_;
std::function<T(T, T)> combine_;
T iota_;
void build(const std::vector<T>& base_array) {
// n to 2n-1 => total n elements => leaves
for (int i = len_; i < 2 * len_; i++) {
seg_tree_[i] = base_array[i - len_];
}
// reverse as back to front is going up the tree
for (int i = len_ - 1; i > 0; i--) {
seg_tree_[i] = combine_(seg_tree_[LEFT(i)], seg_tree_[RIGHT(i)]);
}
}
public:
IterSegTree(const std::vector<T>& base_array,
std::function<T(T, T)> combine_func, T iota_value)
: combine_(combine_func), iota_(iota_value) {
len_ = base_array.size();
seg_tree_.resize(2 * len_ + 1);
build(base_array);
}
IterSegTree(int len, T iota)
: len_(len), iota_(iota), seg_tree_(2 * len_ + 1, iota_) {}
void update(int index, std::function<T(T)> transform) {
// 1. go to leaf and update
index += len_;
seg_tree_[index] = transform(seg_tree_[index]);
while (index > 1) { // we don't use 0
index >>= 1; // move to parent first
seg_tree_[index] =
combine_(seg_tree_[LEFT(index)], seg_tree_[RIGHT(index)]);
}
}
// for range [l,r)
T query(int left, int right) {
left += len_;
right += len_;
T result = iota_;
// NOTE: this soln assumes combine_ is commutative,
// else you need to separarte left and right accumulators
for (; left < right; left >>= 1, right >>= 1) {
if (IS_RIGHT_CHILD(left))
result = combine_(result, seg_tree_[left++]);
if (IS_RIGHT_CHILD(right))
result = combine_(result, seg_tree_[--right]);
}
return result;
}
};
@@ -0,0 +1,45 @@
#pragma once
#include <functional>
#include <vector>
#define LEFT(i) (i << 1)
#define RIGHT(i) ((i << 1) | 1)
#define IS_RIGHT_CHILD(i) ((i) & 1)
// REMEMBER REMEMBER REMEMBER REMEMBER
// Invariant: tree[p] will reflect the effect ot lazy[p]
// the lazy is not for the node, but for the children
template <typename T, typename L>
class IterLazySegTree {
private:
int n_, h_;
std::vector<T> tree_;
std::vector<L> lazy_;
T iota_;
L iota_lazy_;
std::function<T(T, T)> combine_;
std::function<L(L, L)> combine_lazy_;
std::function<T(T, L, int)> apply_lazy_;
void apply(int index, L new_lazy, int len) {
// invariant: tree_[index] has no lazy, so just need the
// new val to be applied
tree_[index] = apply_lazy_(tree_[index], new_lazy, len);
if (index < n_) { // not leaf
// combine lazy for children
lazy_[index] = combine_lazy_(lazy_[index], new_lazy);
}
}
void push(int index) {
for (int cur_h = h_; cur_h > 0; cur_h--) {
int par_at_level_cur_h = index >> cur_h;
if (lazy_[par_at_level_cur_h] != iota_lazy_) {
}
}
}
};
+445
View File
@@ -0,0 +1,445 @@
<!-- AI GENERATED SIMULATION -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Iterative SegTree (Dark Mode)</title>
<style>
:root {
/* Dark Mode Palette */
--bg-body: #0f172a; /* Slate 950 */
--bg-panel: #1e293b; /* Slate 800 */
--text-main: #f1f5f9; /* Slate 100 */
--text-muted: #94a3b8; /* Slate 400 */
--node-bg: #334155; /* Slate 700 */
--node-border: #475569; /* Slate 600 */
/* Accents (Lighter for dark mode visibility) */
--highlight-l: #60a5fa; /* Blue 400 */
--highlight-r: #f87171; /* Red 400 */
--selected: #10b981; /* Emerald 500 */
--selected-bg: #064e3b; /* Emerald 900 */
}
body {
font-family: "Segoe UI", system-ui, sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
margin: 0;
}
h2 {
margin-bottom: 10px;
font-weight: 300;
letter-spacing: 1px;
}
/* --- Controls --- */
.controls {
background: var(--bg-panel);
padding: 15px 25px;
border-radius: 12px;
border: 1px solid var(--node-border);
display: flex;
gap: 15px;
align-items: center;
margin-bottom: 20px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
z-index: 10;
}
input {
width: 40px;
padding: 8px;
text-align: center;
background: var(--bg-body);
border: 1px solid var(--node-border);
color: var(--text-main);
border-radius: 6px;
font-size: 16px;
}
input:focus {
outline: 2px solid var(--highlight-l);
border-color: transparent;
}
button {
padding: 8px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
color: #fff;
}
#btn-start {
background-color: #2563eb;
} /* Blue 600 */
#btn-start:hover {
background-color: #3b82f6;
}
#btn-step {
background-color: #d97706;
} /* Amber 600 */
#btn-step:hover {
background-color: #f59e0b;
}
#btn-reset {
background-color: #475569;
} /* Slate 600 */
#btn-reset:hover {
background-color: #64748b;
}
button:disabled {
opacity: 0.3;
cursor: not-allowed;
filter: grayscale(1);
}
/* --- Info & Results --- */
.info-panel {
width: 100%;
max-width: 800px;
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.explanation {
flex: 2;
background: var(--bg-panel);
padding: 15px;
border-left: 4px solid var(--highlight-l);
border-radius: 6px;
min-height: 60px;
display: flex;
align-items: center;
line-height: 1.5;
}
.result-box {
flex: 1;
background: var(--bg-panel);
padding: 15px;
border-radius: 6px;
border: 1px dashed var(--node-border);
display: flex;
flex-wrap: wrap;
gap: 8px;
align-content: flex-start;
}
.mini-badge {
font-size: 11px;
background: var(--selected-bg);
color: var(--selected);
border: 1px solid var(--selected);
padding: 4px 8px;
border-radius: 4px;
}
/* --- Visualization Area --- */
#viz-container {
width: 800px;
height: 400px;
background: var(--bg-panel);
border-radius: 12px;
border: 1px solid var(--node-border);
position: relative;
box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.2);
}
/* --- Nodes --- */
.node {
position: absolute;
width: 44px;
height: 44px;
background: var(--node-bg);
border: 2px solid var(--node-border);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: var(--text-main);
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: default;
transform: translate(-50%, -50%);
z-index: 2;
}
/* Index label above node */
.node .idx {
position: absolute;
top: -18px;
font-size: 10px;
color: var(--text-muted);
font-weight: normal;
}
/* L/R Pointer label below node */
.node .ptr-label {
position: absolute;
bottom: -28px;
font-size: 16px;
font-weight: 900;
opacity: 0;
transition: opacity 0.2s;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
/* --- Active States --- */
.node.ptr-l {
border-color: var(--highlight-l);
box-shadow: 0 0 15px rgba(96, 165, 250, 0.4);
background: #1e3a8a; /* Darker Blue */
}
.node.ptr-l .ptr-label {
opacity: 1;
color: var(--highlight-l);
}
.node.ptr-r {
border-color: var(--highlight-r);
box-shadow: 0 0 15px rgba(248, 113, 113, 0.4);
background: #450a0a; /* Darker Red */
}
.node.ptr-r .ptr-label {
opacity: 1;
color: var(--highlight-r);
}
.node.selected {
background-color: var(--selected);
color: #fff;
border-color: #059669;
transform: translate(-50%, -50%) scale(1.15);
box-shadow: 0 0 20px rgba(16, 185, 129, 0.4);
}
</style>
</head>
<body>
<h2>Iterative Segment Tree</h2>
<div class="controls">
<label>Range [L, R):</label>
<input type="number" id="lInput" value="1" min="0" max="7" />
<span style="color: var(--text-muted)">to</span>
<input type="number" id="rInput" value="7" min="1" max="8" />
<button id="btn-start" onclick="initQuery()">Start Query</button>
<button id="btn-step" onclick="nextStep()" disabled>Step ▶</button>
<button id="btn-reset" onclick="resetSim()">Reset</button>
</div>
<div class="info-panel">
<div class="explanation" id="explanation">
Ready. Set the range and click "Start Query".
</div>
<div class="result-box" id="result-box">
<small style="width: 100%; color: var(--text-muted); margin-bottom: 5px"
>Accumulator:</small
>
</div>
</div>
<div id="viz-container"></div>
<script>
const n = 8;
const width = 800;
const height = 400;
const levelGap = 90;
let treeData = [];
let coords = {};
let l, r;
let isRunning = false;
let stepState = 0;
// --- Initialization ---
function initTreeData() {
treeData = Array(2 * n).fill(0);
for (let i = 0; i < n; i++) treeData[n + i] = i + 1;
for (let i = n - 1; i > 0; i--)
treeData[i] = treeData[2 * i] + treeData[2 * i + 1];
}
function calculateCoords() {
const segmentWidth = width / n;
for (let i = 0; i < n; i++) {
let idx = n + i;
coords[idx] = {
x: i * segmentWidth + segmentWidth / 2,
y: height - 60,
};
}
for (let i = n - 1; i > 0; i--) {
const leftChild = coords[2 * i];
const rightChild = coords[2 * i + 1];
coords[i] = {
x: (leftChild.x + rightChild.x) / 2,
y: leftChild.y - levelGap,
};
}
}
function renderNodes() {
const container = document.getElementById("viz-container");
container.innerHTML = "";
for (let i = 1; i < 2 * n; i++) {
const pos = coords[i];
const node = document.createElement("div");
node.className = "node";
node.id = `node-${i}`;
node.style.left = pos.x + "px";
node.style.top = pos.y + "px";
node.innerHTML = `
<span class="idx">${i}</span>
${treeData[i]}
<span class="ptr-label" id="ptr-${i}"></span>
`;
container.appendChild(node);
}
}
// --- Visual Helpers ---
function setPointer(idx, label) {
const node = document.getElementById(`node-${idx}`);
const lbl = document.getElementById(`ptr-${idx}`);
if (!node) return;
if (label === "L") node.classList.add("ptr-l");
if (label === "R") node.classList.add("ptr-r");
if (lbl) lbl.textContent = label;
}
function clearPointers() {
for (let i = 1; i < 2 * n; i++) {
const node = document.getElementById(`node-${i}`);
const lbl = document.getElementById(`ptr-${i}`);
node.classList.remove("ptr-l", "ptr-r");
if (lbl) lbl.textContent = "";
}
}
function markSelected(idx) {
const node = document.getElementById(`node-${idx}`);
if (node) node.classList.add("selected");
const box = document.getElementById("result-box");
const badge = document.createElement("div");
badge.className = "mini-badge";
badge.textContent = `idx ${idx} (${treeData[idx]})`;
box.appendChild(badge);
}
function setMsg(html) {
document.getElementById("explanation").innerHTML = html;
}
// --- Logic Control ---
function initQuery() {
resetSim(false);
l = parseInt(document.getElementById("lInput").value) + n;
r = parseInt(document.getElementById("rInput").value) + n;
isRunning = true;
stepState = 1;
document.getElementById("btn-start").disabled = true;
document.getElementById("btn-step").disabled = false;
setMsg(`Initialized at leaves <b>L=${l}</b> and <b>R=${r}</b>.`);
if (l < r) {
setPointer(l, "L");
setPointer(r, "R");
}
}
function nextStep() {
if (!isRunning) return;
if (l >= r) {
clearPointers();
setMsg("<strong>L and R have met.</strong> Query Complete!");
document.getElementById("btn-step").disabled = true;
return;
}
if (stepState === 1) {
// Check L
if (l % 2 === 1) {
setMsg(
`L=${l} is <span style="color:var(--highlight-l)">ODD</span> (Right Child).<br>We <b>PICK Node ${l}</b> and move L right.`,
);
markSelected(l);
l++;
} else {
setMsg(
`L=${l} is <span style="color:var(--text-muted)">EVEN</span> (Left Child).<br>Safe to move up.`,
);
}
clearPointers();
if (l < r) {
setPointer(l, "L");
setPointer(r, "R");
}
stepState = 2;
} else if (stepState === 2) {
// Check R
if (r % 2 === 1) {
r--;
setMsg(
`R is <span style="color:var(--highlight-r)">ODD</span>. Range ends after a Left Child.<br>We <b>PICK Node ${r}</b>.`,
);
markSelected(r);
} else {
setMsg(
`R=${r} is <span style="color:var(--text-muted)">EVEN</span>. Boundary is clean.`,
);
}
clearPointers();
if (l < r) {
setPointer(l, "L");
setPointer(r, "R");
}
stepState = 3;
} else if (stepState === 3) {
// Move Up
l = Math.floor(l / 2);
r = Math.floor(r / 2);
setMsg(
`Move both pointers UP to parents.<br>New L=${l}, New R=${r}.`,
);
clearPointers();
if (l < r) {
setPointer(l, "L");
setPointer(r, "R");
}
stepState = 1;
}
}
function resetSim(full = true) {
isRunning = false;
document.getElementById("btn-start").disabled = false;
document.getElementById("btn-step").disabled = true;
if (full) setMsg("Ready.");
document.getElementById("result-box").innerHTML =
'<small style="width:100%; color:var(--text-muted); margin-bottom:5px;">Accumulator:</small>';
const nodes = document.querySelectorAll(".node");
nodes.forEach((n) => {
n.className = "node";
n.querySelector(".ptr-label").textContent = "";
});
}
initTreeData();
calculateCoords();
renderNodes();
</script>
</body>
</html>
+14
View File
@@ -0,0 +1,14 @@
#include <iostream>
#include <vector>
// this isn't ideal
#include "../include/iter_seg_tree.hpp"
int main() {
auto sum = [](int a, int b) { return a + b; };
std::vector<int> vec = {1, 2, 3, 4};
IterSegTree<int> st(vec, sum, 0);
std::cout << st.query(0, 2) << "\n";
}