From a8fb131aef0b606171ffd92e43ddc1f6dbef5ffd Mon Sep 17 00:00:00 2001 From: Sohan Rout Date: Mon, 3 Aug 2026 16:48:41 +0530 Subject: [PATCH] Feat : added lowest common ancestor --- app/visualizer/page.jsx | 6 +- .../trees/advanced/b-trees/page.jsx | 2 + .../trees/advanced/fenwick-tree/animation.jsx | 442 ++++++++++++++++ .../trees/advanced/fenwick-tree/code.js | 183 +++++++ .../trees/advanced/fenwick-tree/content.jsx | 199 ++++++++ .../trees/advanced/fenwick-tree/page.jsx | 110 ++++ .../trees/advanced/fenwick-tree/quiz.jsx | 374 ++++++++++++++ .../trees/advanced/prefix-tree/page.jsx | 2 + .../trees/advanced/red-black/page.jsx | 2 + .../advanced/segment-trees/animation.jsx | 471 ++++++++++++++++++ .../trees/advanced/segment-trees/code.js | 205 ++++++++ .../trees/advanced/segment-trees/content.jsx | 231 +++++++++ .../trees/advanced/segment-trees/page.jsx | 109 ++++ .../trees/advanced/segment-trees/quiz.jsx | 379 ++++++++++++++ .../lowest-common-ancestor/animation.jsx | 432 ++++++++++++++++ .../algorithms/lowest-common-ancestor/code.js | 170 +++++++ .../lowest-common-ancestor/content.jsx | 227 +++++++++ .../lowest-common-ancestor/page.jsx | 117 +++++ .../lowest-common-ancestor/quiz.jsx | 379 ++++++++++++++ app/visualizer/trees/bst/searching/page.jsx | 1 + lib/modulesMap.js | 3 + 21 files changed, 4041 insertions(+), 3 deletions(-) create mode 100755 app/visualizer/trees/advanced/fenwick-tree/animation.jsx create mode 100755 app/visualizer/trees/advanced/fenwick-tree/code.js create mode 100755 app/visualizer/trees/advanced/fenwick-tree/content.jsx create mode 100755 app/visualizer/trees/advanced/fenwick-tree/page.jsx create mode 100755 app/visualizer/trees/advanced/fenwick-tree/quiz.jsx create mode 100755 app/visualizer/trees/advanced/segment-trees/animation.jsx create mode 100755 app/visualizer/trees/advanced/segment-trees/code.js create mode 100755 app/visualizer/trees/advanced/segment-trees/content.jsx create mode 100755 app/visualizer/trees/advanced/segment-trees/page.jsx create mode 100755 app/visualizer/trees/advanced/segment-trees/quiz.jsx create mode 100755 app/visualizer/trees/algorithms/lowest-common-ancestor/animation.jsx create mode 100755 app/visualizer/trees/algorithms/lowest-common-ancestor/code.js create mode 100755 app/visualizer/trees/algorithms/lowest-common-ancestor/content.jsx create mode 100755 app/visualizer/trees/algorithms/lowest-common-ancestor/page.jsx create mode 100755 app/visualizer/trees/algorithms/lowest-common-ancestor/quiz.jsx diff --git a/app/visualizer/page.jsx b/app/visualizer/page.jsx index a0d28ab..0dcace7 100755 --- a/app/visualizer/page.jsx +++ b/app/visualizer/page.jsx @@ -390,8 +390,8 @@ const sections = [ name: "Trie (Prefix Tree)", path: "/visualizer/trees/advanced/prefix-tree", }, - { name: "Segment Trees", path: "/visualizer/trees/advanced/segment" }, - { name: "Fenwick Trees", path: "/visualizer/trees/advanced/fenwick" }, + { name: "Segment Trees", path: "/visualizer/trees/advanced/segment-trees" }, + { name: "Fenwick Trees", path: "/visualizer/trees/advanced/fenwick-tree" }, ], }, { @@ -399,7 +399,7 @@ const sections = [ items: [ { name: "Lowest Common Ancestor", - path: "/visualizer/trees/algorithms/lca", + path: "/visualizer/trees/algorithms/lowest-common-ancestor", }, { name: "Tree Diameter", diff --git a/app/visualizer/trees/advanced/b-trees/page.jsx b/app/visualizer/trees/advanced/b-trees/page.jsx index 71f421c..045ea51 100755 --- a/app/visualizer/trees/advanced/b-trees/page.jsx +++ b/app/visualizer/trees/advanced/b-trees/page.jsx @@ -95,6 +95,8 @@ export default function Page() { links={[ { text: "Red-Black Tree", url: "./red-black" }, { text: "Trie (Prefix Tree)", url: "./prefix-tree" }, + { text: "Segment Tree", url: "./segment-trees" }, + { text: "Fenwick Tree", url: "./fenwick-tree" }, { text: "AVL Balancing", url: "../bst/avl" }, { text: "BST Insertion", url: "../bst/insertion" }, { text: "In-order Traversal", url: "../traversal/in-order" }, diff --git a/app/visualizer/trees/advanced/fenwick-tree/animation.jsx b/app/visualizer/trees/advanced/fenwick-tree/animation.jsx new file mode 100755 index 0000000..3a4cb2b --- /dev/null +++ b/app/visualizer/trees/advanced/fenwick-tree/animation.jsx @@ -0,0 +1,442 @@ +"use client"; +import React, { useState } from "react"; +import { gsap } from "gsap"; +import { Shuffle, RefreshCw, Search, ArrowRightLeft } from "lucide-react"; + +const N = 8; + +const randomArray = () => Array.from({ length: N }, () => Math.floor(Math.random() * 20) + 1); + +const lowbit = (i) => i & -i; + +const updateFenwick = (bit, n, index, delta, path) => { + let i = index + 1; // 1-indexed + while (i <= n) { + bit[i] += delta; + if (path) path.push(i); + i += lowbit(i); + } +}; + +const buildFenwick = (arr) => { + const n = arr.length; + const bit = new Array(n + 1).fill(0); + for (let idx = 0; idx < n; idx++) { + updateFenwick(bit, n, idx, arr[idx]); + } + return bit; +}; + +const prefixSum = (bit, index, path) => { + let i = index + 1; // 1-indexed + let sum = 0; + while (i > 0) { + sum += bit[i]; + if (path) path.push(i); + i -= lowbit(i); + } + return sum; +}; + +const BOX = 42; +const GAP = 6; +const STEP_DELAY = 550; + +const FenwickVisualizer = () => { + const [arr, setArr] = useState(null); + const [bit, setBit] = useState(null); + const [message, setMessage] = useState("No Fenwick tree yet — build one over a random array"); + const [updateIndex, setUpdateIndex] = useState(""); + const [updateValue, setUpdateValue] = useState(""); + const [queryIndex, setQueryIndex] = useState(""); + const [rangeL, setRangeL] = useState(""); + const [rangeR, setRangeR] = useState(""); + const [highlightUpdate, setHighlightUpdate] = useState([]); + const [highlightAdd, setHighlightAdd] = useState([]); + const [highlightSub, setHighlightSub] = useState([]); + const [busy, setBusy] = useState(false); + + const animateDropIn = (el) => { + if (!el || el.dataset.animated) return; + el.dataset.animated = "true"; + gsap.fromTo(el, { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.4, ease: "back.out(1.7)" }); + }; + + const clearHighlights = () => { + setHighlightUpdate([]); + setHighlightAdd([]); + setHighlightSub([]); + }; + + const buildRandom = () => { + if (busy) return; + const newArr = randomArray(); + setArr(newArr); + setBit(buildFenwick(newArr)); + setMessage(`Built a Fenwick tree over [${newArr.join(", ")}]`); + clearHighlights(); + }; + + const handleUpdate = () => { + if (busy || !bit) return; + const index = parseInt(updateIndex, 10); + const value = parseInt(updateValue, 10); + if (Number.isNaN(index) || index < 0 || index >= N) { + setMessage(`Enter an index between 0 and ${N - 1}`); + return; + } + if (Number.isNaN(value)) { + setMessage("Enter a valid new value"); + return; + } + setBusy(true); + clearHighlights(); + + const delta = value - arr[index]; + const newBit = [...bit]; + const path = []; + updateFenwick(newBit, N, index, delta, path); + const newArr = [...arr]; + newArr[index] = value; + + let i = 0; + const revealStep = () => { + setHighlightUpdate(path.slice(0, i + 1)); + setMessage(`Updating index ${index} by ${delta >= 0 ? "+" : ""}${delta} — adding to BIT[${path[i]}]`); + i++; + if (i < path.length) { + setTimeout(revealStep, STEP_DELAY); + } else { + setTimeout(() => { + setBit(newBit); + setArr(newArr); + setMessage(`Index ${index} updated to ${value} — propagated to ${path.length} BIT node(s): [${path.join(", ")}]`); + setTimeout(() => setHighlightUpdate([]), 900); + setBusy(false); + }, STEP_DELAY); + } + }; + revealStep(); + }; + + const handlePrefixQuery = () => { + if (busy || !bit) return; + const index = parseInt(queryIndex, 10); + if (Number.isNaN(index) || index < 0 || index >= N) { + setMessage(`Enter an index between 0 and ${N - 1}`); + return; + } + setBusy(true); + clearHighlights(); + + const path = []; + const sum = prefixSum(bit, index, path); + + let i = 0; + const revealStep = () => { + setHighlightAdd(path.slice(0, i + 1)); + setMessage(`Summing BIT[${path[i]}] — accumulated so far`); + i++; + if (i < path.length) { + setTimeout(revealStep, STEP_DELAY); + } else { + setTimeout(() => { + setMessage(`Prefix sum [0, ${index}] = ${sum}`); + setBusy(false); + }, STEP_DELAY); + } + }; + revealStep(); + }; + + const handleRangeQuery = () => { + if (busy || !bit) return; + const l = parseInt(rangeL, 10); + const r = parseInt(rangeR, 10); + if (Number.isNaN(l) || Number.isNaN(r) || l < 0 || r >= N || l > r) { + setMessage(`Enter a valid range with 0 <= l <= r <= ${N - 1}`); + return; + } + setBusy(true); + clearHighlights(); + + const pathR = []; + const sumR = prefixSum(bit, r, pathR); + const pathL = []; + const sumL = l > 0 ? prefixSum(bit, l - 1, pathL) : 0; + + let i = 0; + const revealAdd = () => { + setHighlightAdd(pathR.slice(0, i + 1)); + setMessage(`Computing prefix sum [0, ${r}] — summing BIT[${pathR[i]}]`); + i++; + if (i < pathR.length) { + setTimeout(revealAdd, STEP_DELAY); + } else { + setTimeout(revealSub, STEP_DELAY); + } + }; + + let j = 0; + const revealSub = () => { + if (pathL.length === 0) { + finish(); + return; + } + setHighlightSub(pathL.slice(0, j + 1)); + setMessage(`Computing prefix sum [0, ${l - 1}] to subtract — summing BIT[${pathL[j]}]`); + j++; + if (j < pathL.length) { + setTimeout(revealSub, STEP_DELAY); + } else { + setTimeout(finish, STEP_DELAY); + } + }; + + const finish = () => { + setMessage(`Range sum [${l}, ${r}] = prefixSum(${r}) - prefixSum(${l - 1}) = ${sumR} - ${sumL} = ${sumR - sumL}`); + setBusy(false); + }; + + revealAdd(); + }; + + const reset = () => { + if (busy) return; + setArr(null); + setBit(null); + setMessage("No Fenwick tree yet — build one over a random array"); + setUpdateIndex(""); + setUpdateValue(""); + setQueryIndex(""); + setRangeL(""); + setRangeR(""); + clearHighlights(); + }; + + const maxLevel = Math.ceil(Math.log2(N + 1)); + const arrayWidth = N * BOX + (N - 1) * GAP; + + return ( +
+

+ Build a Fenwick tree (Binary Indexed Tree), then update a value or query a range +

+ +
+ {/* Controls */} +
+ + +
+ setUpdateIndex(e.target.value)} + placeholder={`index (0-${N - 1})`} + disabled={busy || !bit} + className="w-28 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500 transition disabled:opacity-50" + /> + setUpdateValue(e.target.value)} + placeholder="new value" + disabled={busy || !bit} + className="w-28 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500 transition disabled:opacity-50" + /> + +
+ +
+ setQueryIndex(e.target.value)} + placeholder="up to index" + disabled={busy || !bit} + className="w-28 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500 transition disabled:opacity-50" + /> + +
+ +
+ setRangeL(e.target.value)} + placeholder="from l" + disabled={busy || !bit} + className="w-24 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500 transition disabled:opacity-50" + /> + setRangeR(e.target.value)} + placeholder="to r" + disabled={busy || !bit} + className="w-24 px-3 py-2 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-neutral-900 text-gray-800 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500 transition disabled:opacity-50" + /> + + +
+
+ + {/* Visualization */} +
+
+ {message} +
+ + {arr && bit ? ( +
+
+ {/* Original array */} +
Original array (0-indexed)
+
+ {arr.map((v, i) => ( +
+ {v} +
+ ))} +
+
+ {arr.map((_, i) => ( +
+ {i} +
+ ))} +
+ + {/* BIT array */} +
Fenwick (BIT) array (1-indexed)
+
+ {bit.slice(1).map((v, idx) => { + const i = idx + 1; + const isUpdate = highlightUpdate.includes(i); + const isAdd = highlightAdd.includes(i); + const isSub = highlightSub.includes(i); + return ( +
+ {v} +
+ ); + })} +
+
+ {bit.slice(1).map((_, idx) => ( +
+ {idx + 1} +
+ ))} +
+ + {/* Responsibility ladder */} +
+ Which original range each BIT index is responsible for +
+ + {Array.from({ length: N }, (_, idx) => idx + 1).map((i) => { + const width = lowbit(i); + const level = Math.log2(width); + const startCol = i - width; // 0-indexed start column + const x = startCol * (BOX + GAP); + const barWidth = width * BOX + (width - 1) * GAP; + const y = maxLevel * 24 - level * 24 - 16; + const isHighlighted = highlightUpdate.includes(i) || highlightAdd.includes(i) || highlightSub.includes(i); + const isUpdate = highlightUpdate.includes(i); + const isSub = highlightSub.includes(i); + const fill = isUpdate ? "#8b5cf6" : isSub ? "#f43f5e" : isHighlighted ? "#f59e0b" : "#93c5fd"; + return ( + + + + BIT[{i}] + + + ); + })} + +
+
+ ) : ( +
+ No Fenwick tree yet — build one over a random array +
+ )} + +
+ + + BIT node + + + + Update path + + + + Prefix sum path (added) + + + + Subtracted prefix (for range queries) + +
+
+
+
+ ); +}; + +export default FenwickVisualizer; diff --git a/app/visualizer/trees/advanced/fenwick-tree/code.js b/app/visualizer/trees/advanced/fenwick-tree/code.js new file mode 100755 index 0000000..76c13ba --- /dev/null +++ b/app/visualizer/trees/advanced/fenwick-tree/code.js @@ -0,0 +1,183 @@ +const codeExamples = { + javascript: `// Fenwick Tree (Binary Indexed Tree) for prefix/range sums +class FenwickTree { + constructor(size) { + this.n = size; + this.bit = new Array(size + 1).fill(0); // 1-indexed + } + + static fromArray(arr) { + const tree = new FenwickTree(arr.length); + arr.forEach((value, index) => tree.update(index, value)); + return tree; + } + + // Add 'delta' to the element at 0-indexed 'index' + update(index, delta) { + let i = index + 1; // convert to 1-indexed + while (i <= this.n) { + this.bit[i] += delta; + i += i & -i; // move to the next range that includes this index + } + } + + // Sum of elements from 0 to index (inclusive), 0-indexed + prefixSum(index) { + let i = index + 1; + let sum = 0; + while (i > 0) { + sum += this.bit[i]; + i -= i & -i; // move down to the next chunk of the prefix + } + return sum; + } + + // Sum of elements from l to r (inclusive), 0-indexed + rangeSum(l, r) { + return this.prefixSum(r) - (l > 0 ? this.prefixSum(l - 1) : 0); + } +} + +// Usage example +const tree = FenwickTree.fromArray([2, 5, 1, 4, 9, 3]); +tree.rangeSum(1, 3); // 5 + 1 + 4 = 10 +tree.update(2, 9); // add 9 to index 2 (was 1, becomes 10) +tree.rangeSum(1, 3); // 5 + 10 + 4 = 19`, + + python: `# Fenwick Tree (Binary Indexed Tree) for prefix/range sums +class FenwickTree: + def __init__(self, size): + self.n = size + self.bit = [0] * (size + 1) # 1-indexed + + @classmethod + def from_array(cls, arr): + tree = cls(len(arr)) + for index, value in enumerate(arr): + tree.update(index, value) + return tree + + def update(self, index, delta): + """Add delta to the element at 0-indexed index.""" + i = index + 1 # convert to 1-indexed + while i <= self.n: + self.bit[i] += delta + i += i & (-i) # move to the next range that includes this index + + def prefix_sum(self, index): + """Sum of elements from 0 to index (inclusive), 0-indexed.""" + i = index + 1 + total = 0 + while i > 0: + total += self.bit[i] + i -= i & (-i) # move down to the next chunk of the prefix + return total + + def range_sum(self, l, r): + """Sum of elements from l to r (inclusive), 0-indexed.""" + return self.prefix_sum(r) - (self.prefix_sum(l - 1) if l > 0 else 0) + +# Usage example +tree = FenwickTree.from_array([2, 5, 1, 4, 9, 3]) +tree.range_sum(1, 3) # 5 + 1 + 4 = 10 +tree.update(2, 9) # add 9 to index 2 (was 1, becomes 10) +tree.range_sum(1, 3) # 5 + 10 + 4 = 19`, + + c: `#include + +#define MAXN 100 + +int bit[MAXN + 1]; // 1-indexed +int n; + +// Add delta to the element at 0-indexed index +void update(int index, int delta) { + int i = index + 1; // convert to 1-indexed + while (i <= n) { + bit[i] += delta; + i += i & (-i); // move to the next range that includes this index + } +} + +// Sum of elements from 0 to index (inclusive), 0-indexed +int prefixSum(int index) { + int i = index + 1; + int sum = 0; + while (i > 0) { + sum += bit[i]; + i -= i & (-i); // move down to the next chunk of the prefix + } + return sum; +} + +// Sum of elements from l to r (inclusive), 0-indexed +int rangeSum(int l, int r) { + return prefixSum(r) - (l > 0 ? prefixSum(l - 1) : 0); +} + +int main() { + int arr[] = {2, 5, 1, 4, 9, 3}; + n = 6; + for (int i = 0; i < n; i++) update(i, arr[i]); + + printf("rangeSum(1,3) = %d\\n", rangeSum(1, 3)); // 10 + + update(2, 9); // add 9 to index 2 (was 1, becomes 10) + printf("rangeSum(1,3) after update = %d\\n", rangeSum(1, 3)); // 19 + + return 0; +}`, + + java: `// Fenwick Tree (Binary Indexed Tree) for prefix/range sums +public class FenwickTree { + private final int[] bit; // 1-indexed + private final int n; + + public FenwickTree(int size) { + n = size; + bit = new int[size + 1]; + } + + public static FenwickTree fromArray(int[] arr) { + FenwickTree tree = new FenwickTree(arr.length); + for (int i = 0; i < arr.length; i++) { + tree.update(i, arr[i]); + } + return tree; + } + + // Add delta to the element at 0-indexed index + public void update(int index, int delta) { + int i = index + 1; // convert to 1-indexed + while (i <= n) { + bit[i] += delta; + i += i & (-i); // move to the next range that includes this index + } + } + + // Sum of elements from 0 to index (inclusive), 0-indexed + public int prefixSum(int index) { + int i = index + 1; + int sum = 0; + while (i > 0) { + sum += bit[i]; + i -= i & (-i); // move down to the next chunk of the prefix + } + return sum; + } + + // Sum of elements from l to r (inclusive), 0-indexed + public int rangeSum(int l, int r) { + return prefixSum(r) - (l > 0 ? prefixSum(l - 1) : 0); + } + + public static void main(String[] args) { + FenwickTree tree = FenwickTree.fromArray(new int[]{2, 5, 1, 4, 9, 3}); + System.out.println(tree.rangeSum(1, 3)); // 10 + tree.update(2, 9); // add 9 to index 2 (was 1, becomes 10) + System.out.println(tree.rangeSum(1, 3)); // 19 + } +}`, +}; + +export default codeExamples; diff --git a/app/visualizer/trees/advanced/fenwick-tree/content.jsx b/app/visualizer/trees/advanced/fenwick-tree/content.jsx new file mode 100755 index 0000000..d1e8daa --- /dev/null +++ b/app/visualizer/trees/advanced/fenwick-tree/content.jsx @@ -0,0 +1,199 @@ +"use client"; +import ComplexityGraph from "@/app/components/ui/graph"; +import { useTheme } from "@/app/contexts/ThemeContext"; +import DailyDSAEmbed from "@/app/components/ui/DailyDSAEmbed"; +import NewsletterEmbed from "@/app/components/ui/NewsletterEmbed"; +import InContentAd from "@/app/components/ads/InContentAd"; +import { motion } from "framer-motion"; + +const lowbit = (i) => i & -i; +const BOX = 30; +const GAP = 6; + +const ResponsibilityDiagram = () => { + const N = 8; + const maxLevel = 4; + const arrayWidth = N * BOX + (N - 1) * GAP; + + return ( + + {Array.from({ length: N }, (_, idx) => idx + 1).map((i, order) => { + const width = lowbit(i); + const level = Math.log2(width); + const startCol = i - width; + const x = startCol * (BOX + GAP); + const barWidth = width * BOX + (width - 1) * GAP; + const y = maxLevel * 20 - level * 20 - 14; + return ( + + + + {i} + + + ); + })} + + ); +}; + +const Content = () => { + const { theme } = useTheme(); + + const paragraphs = [ + `A Fenwick Tree (also called a Binary Indexed Tree, or BIT) solves the same core problem a Segment Tree does — prefix/range sums with fast point updates — but with a much smaller footprint: one plain array, no explicit tree nodes or pointers at all. The "tree" is implicit, encoded entirely in the binary representation of each index.`, + `Every index i in the BIT array is responsible for a range of the original array whose length is exactly the value of i's lowest set bit (its "lowbit"). Index 6 in binary is 110, whose lowest set bit is 2, so BIT[6] covers a range of 2 elements. Index 8 is 1000, whose lowest set bit is 8, so BIT[8] covers all 8 elements up to it. This single bit trick is the entire structure — no recursion, no children pointers, just index arithmetic.`, + `Moving between indices uses that same lowbit value: adding it to an index walks up toward larger ranges (used when propagating a point update to every range that includes it), and subtracting it walks down toward smaller ranges (used when accumulating a prefix sum). Both walks take exactly O(log n) steps, because each step clears or sets one more bit in the index.`, + `The tradeoff for this smaller footprint is flexibility: a Fenwick Tree's range query only works by combining prefix sums (range[l,r] = prefix(r) - prefix(l-1)), which requires the underlying operation to have an inverse. That works great for sum, but not for operations like minimum or maximum, which don't have an inverse — a Segment Tree is needed for those instead.`, + ]; + + const algorithm = [ + { points: "Build: start with an all-zero BIT array, then apply a point update for every element of the input array" }, + { + points: "Point Update(index, delta) — add delta to the element at index:", + subpoints: [ + "Convert to 1-indexed: i = index + 1", + "While i is within bounds: add delta to BIT[i], then move to the next responsible index with i += lowbit(i)", + ], + }, + { + points: "Prefix Sum(index) — sum of everything from 0 to index:", + subpoints: [ + "Convert to 1-indexed: i = index + 1", + "While i > 0: add BIT[i] to the running total, then move down with i -= lowbit(i)", + ], + }, + { points: "Range Sum(l, r) = Prefix Sum(r) - Prefix Sum(l - 1)" }, + ]; + + const complexity = [ + { points: "Build: O(n log n) naively (n point updates), or O(n) with a direct construction trick." }, + { points: "Point Update: O(log n) — one walk upward through responsible ranges." }, + { points: "Prefix/Range Query: O(log n) — one walk downward accumulating partial sums." }, + ]; + + return ( +
+
+ + +
+
+ {/* What is a Fenwick Tree */} +
+

+ + What is a Fenwick Tree? +

+
+

+ {paragraphs[0]} +

+
+
+ + {/* How the implicit structure works */} +
+

+ + How Does the Implicit Structure Work? +

+
+

+ {paragraphs[1]} +

+
+ +
+ Each BIT index's responsibility range, sized by its lowest set bit +
+ + +

+ {paragraphs[2]} +

+
+ + {/* Tradeoff vs segment tree */} +
+

+ + Fenwick Tree vs. Segment Tree +

+
+

+ {paragraphs[3]} +

+
+
+ + {/* Algorithm Steps */} +
+

+ + Algorithm Steps +

+
+
    + {algorithm.map((item, index) => ( +
  1. + {item.points} + {item.subpoints && ( +
      + {item.subpoints.map((subitem, subindex) => ( +
    • + {subitem} +
    • + ))} +
    + )} +
  2. + ))} +
+
+
+ + {/* Time Complexity */} +
+

+ + Time Complexity +

+
+
    + {complexity.map((item, index) => ( +
  • + + {item.points.split(":")[0]}: + + {item.points.split(":")[1]} +
  • + ))} +
+
+ +
+ Math.log2(n)} + averageCase={(n) => Math.log2(n)} + worstCase={(n) => Math.log2(n)} + maxN={25} + /> +
+ + +
+
+ + +
+ ); +}; + +export default Content; diff --git a/app/visualizer/trees/advanced/fenwick-tree/page.jsx b/app/visualizer/trees/advanced/fenwick-tree/page.jsx new file mode 100755 index 0000000..c5d7dca --- /dev/null +++ b/app/visualizer/trees/advanced/fenwick-tree/page.jsx @@ -0,0 +1,110 @@ +import Animation from "@/app/visualizer/trees/advanced/fenwick-tree/animation"; +import Navbar from "@/app/components/navbarinner"; +import ModuleHeader from "@/app/components/modules/Header"; +import Footer from "@/app/components/footer"; +import BackToTop from "@/app/components/ui/backtotop"; +import ExploreOther from "@/app/components/ui/exploreOther"; +import CodeBlock from "@/app/components/modules/CodeBlock"; +import codeExamples from "./code"; +import Quiz from "@/app/visualizer/trees/advanced/fenwick-tree/quiz"; +import Content from "@/app/visualizer/trees/advanced/fenwick-tree/content"; +import ModuleCard from "@/app/components/ui/ModuleCard"; +import { MODULE_MAPS } from "@/lib/modulesMap"; + +export const metadata = { + title: "Fenwick Tree (Binary Indexed Tree) | Update & Query Animation and Explanation", + description: + "Learn how a Fenwick Tree (Binary Indexed Tree) answers prefix and range sum queries with fast point updates using just one array and the lowbit trick, with an interactive visualizer, code examples in JavaScript, C, Python, and Java, and a quiz.", + keywords: [ + "Fenwick Tree", + "Binary Indexed Tree", + "BIT Data Structure", + "Fenwick Tree Update", + "Fenwick Tree Query", + "Prefix Sum Query", + "Range Sum Query", + "Fenwick Tree vs Segment Tree", + "Fenwick Tree Visualization", + "Fenwick Tree in JavaScript", + "Fenwick Tree in C", + "Fenwick Tree in Python", + "Fenwick Tree in Java", + "Advanced Trees", + "DSA Trees", + "Tree Quiz", + ], + robots: "index, follow", + openGraph: { + images: [ + { + url: "/og.png", + width: 1200, + height: 630, + alt: "Fenwick Tree (Binary Indexed Tree) Visualization", + }, + ], + }, +}; + +export default function Page() { + const paths = [ + { name: "Home", href: "/" }, + { name: "Visualizer", href: "/visualizer" }, + { name: "Trees : Fenwick Tree", href: "" }, + ]; + + return ( + <> +
+ +
+ +
+
+ + +
+ +
+ +
+ +
+

+ Test Your Knowledge before moving forward! +

+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +