1047-flat-tree — Flat Binary Tree in Vec
Tutorial
The Problem
Binary heaps and segment trees store binary trees in arrays using the heap indexing formula: for a node at index i, the left child is at 2*i+1, the right child at 2*i+2, and the parent at (i-1)/2. This array-based tree representation eliminates pointer overhead, maximizes cache locality, and enables O(1) parent/child navigation without storing explicit pointers.
This pattern is the implementation technique behind binary heaps (BinaryHeap), segment trees for range queries, and complete binary trees in competitive programming.
🎯 Learning Outcomes
Vec<T> using heap indexingCode Example
#![allow(clippy::all)]
// 1047: Flat Binary Tree in Vec
// Children of node i: left = 2*i+1, right = 2*i+2, parent = (i-1)/2
struct FlatTree<T> {
data: Vec<T>,
}
impl<T: std::fmt::Debug + Clone + Ord> FlatTree<T> {
fn new(data: Vec<T>) -> Self {
FlatTree { data }
}
fn left_child(i: usize) -> usize {
2 * i + 1
}
fn right_child(i: usize) -> usize {
2 * i + 2
}
fn parent(i: usize) -> usize {
(i - 1) / 2
}
fn get(&self, i: usize) -> Option<&T> {
self.data.get(i)
}
fn is_leaf(&self, i: usize) -> bool {
Self::left_child(i) >= self.data.len()
}
fn left(&self, i: usize) -> Option<&T> {
self.data.get(Self::left_child(i))
}
fn right(&self, i: usize) -> Option<&T> {
self.data.get(Self::right_child(i))
}
/// Level-order traversal (returns levels)
fn levels(&self) -> Vec<Vec<&T>> {
let mut result = Vec::new();
let mut start = 0;
let mut level_size = 1;
while start < self.data.len() {
let end = (start + level_size).min(self.data.len());
result.push(self.data[start..end].iter().collect());
start = end;
level_size *= 2;
}
result
}
/// Heapify: build max-heap in place
fn heapify(&mut self) {
let n = self.data.len();
for i in (0..n / 2).rev() {
self.sift_down(i);
}
}
fn sift_down(&mut self, mut i: usize) {
let n = self.data.len();
loop {
let mut largest = i;
let l = Self::left_child(i);
let r = Self::right_child(i);
if l < n && self.data[l] > self.data[largest] {
largest = l;
}
if r < n && self.data[r] > self.data[largest] {
largest = r;
}
if largest == i {
break;
}
self.data.swap(i, largest);
i = largest;
}
}
/// Depth of the tree
fn depth(&self) -> usize {
if self.data.is_empty() {
0
} else {
(self.data.len() as f64).log2().floor() as usize + 1
}
}
}
fn basic_tree() {
// 1
// / \
// 2 3
// / \ /
// 4 5 6
let tree = FlatTree::new(vec![1, 2, 3, 4, 5, 6]);
assert_eq!(tree.get(0), Some(&1)); // Root
assert_eq!(tree.left(0), Some(&2));
assert_eq!(tree.right(0), Some(&3));
assert_eq!(tree.left(1), Some(&4));
assert_eq!(tree.right(1), Some(&5));
assert_eq!(tree.left(2), Some(&6));
assert_eq!(tree.right(2), None); // No right child for node 2
assert!(tree.is_leaf(3));
assert!(!tree.is_leaf(0));
}
fn level_order_test() {
let tree = FlatTree::new(vec![1, 2, 3, 4, 5, 6, 7]);
let levels = tree.levels();
assert_eq!(levels.len(), 3);
assert_eq!(levels[0], vec![&1]);
assert_eq!(levels[1], vec![&2, &3]);
assert_eq!(levels[2], vec![&4, &5, &6, &7]);
}
fn heap_test() {
let mut tree = FlatTree::new(vec![3, 1, 4, 1, 5, 9, 2, 6]);
tree.heapify();
// Root should be max
assert_eq!(tree.get(0), Some(&9));
// Heap property: parent >= children
for i in 1..tree.data.len() {
let p = FlatTree::<i32>::parent(i);
assert!(
tree.data[p] >= tree.data[i],
"Heap violation: parent[{}]={:?} < child[{}]={:?}",
p,
tree.data[p],
i,
tree.data[i]
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
basic_tree();
}
#[test]
fn test_levels() {
level_order_test();
}
#[test]
fn test_heap() {
heap_test();
}
#[test]
fn test_depth() {
assert_eq!(FlatTree::new(vec![1]).depth(), 1);
assert_eq!(FlatTree::new(vec![1, 2, 3]).depth(), 2);
assert_eq!(FlatTree::new(vec![1, 2, 3, 4, 5, 6, 7]).depth(), 3);
}
#[test]
fn test_empty() {
let tree: FlatTree<i32> = FlatTree::new(vec![]);
assert_eq!(tree.depth(), 0);
assert_eq!(tree.get(0), None);
}
}Key Differences
2*i+1, 2*i+2, (i-1)/2 index arithmetic — this is mathematical, not language-specific.Vec::get(i) returns Option<&T> for safe out-of-bounds access; OCaml requires manual bounds checking or raises Invalid_argument.BinaryHeap::sort uses this exact representation internally; OCaml's Array.sort uses a different algorithm.FlatTree<T: Ord> uses trait bounds for comparison; OCaml uses polymorphic comparison or a functor.OCaml Approach
OCaml's flat tree uses an array with the same index arithmetic:
let left_child i = 2 * i + 1
let right_child i = 2 * i + 2
let parent i = (i - 1) / 2
let is_leaf arr i = left_child i >= Array.length arr
let get arr i =
if i < Array.length arr then Some arr.(i) else None
The formulas are identical. OCaml arrays are mutable by default, enabling in-place heap sort.
Full Source
#![allow(clippy::all)]
// 1047: Flat Binary Tree in Vec
// Children of node i: left = 2*i+1, right = 2*i+2, parent = (i-1)/2
struct FlatTree<T> {
data: Vec<T>,
}
impl<T: std::fmt::Debug + Clone + Ord> FlatTree<T> {
fn new(data: Vec<T>) -> Self {
FlatTree { data }
}
fn left_child(i: usize) -> usize {
2 * i + 1
}
fn right_child(i: usize) -> usize {
2 * i + 2
}
fn parent(i: usize) -> usize {
(i - 1) / 2
}
fn get(&self, i: usize) -> Option<&T> {
self.data.get(i)
}
fn is_leaf(&self, i: usize) -> bool {
Self::left_child(i) >= self.data.len()
}
fn left(&self, i: usize) -> Option<&T> {
self.data.get(Self::left_child(i))
}
fn right(&self, i: usize) -> Option<&T> {
self.data.get(Self::right_child(i))
}
/// Level-order traversal (returns levels)
fn levels(&self) -> Vec<Vec<&T>> {
let mut result = Vec::new();
let mut start = 0;
let mut level_size = 1;
while start < self.data.len() {
let end = (start + level_size).min(self.data.len());
result.push(self.data[start..end].iter().collect());
start = end;
level_size *= 2;
}
result
}
/// Heapify: build max-heap in place
fn heapify(&mut self) {
let n = self.data.len();
for i in (0..n / 2).rev() {
self.sift_down(i);
}
}
fn sift_down(&mut self, mut i: usize) {
let n = self.data.len();
loop {
let mut largest = i;
let l = Self::left_child(i);
let r = Self::right_child(i);
if l < n && self.data[l] > self.data[largest] {
largest = l;
}
if r < n && self.data[r] > self.data[largest] {
largest = r;
}
if largest == i {
break;
}
self.data.swap(i, largest);
i = largest;
}
}
/// Depth of the tree
fn depth(&self) -> usize {
if self.data.is_empty() {
0
} else {
(self.data.len() as f64).log2().floor() as usize + 1
}
}
}
fn basic_tree() {
// 1
// / \
// 2 3
// / \ /
// 4 5 6
let tree = FlatTree::new(vec![1, 2, 3, 4, 5, 6]);
assert_eq!(tree.get(0), Some(&1)); // Root
assert_eq!(tree.left(0), Some(&2));
assert_eq!(tree.right(0), Some(&3));
assert_eq!(tree.left(1), Some(&4));
assert_eq!(tree.right(1), Some(&5));
assert_eq!(tree.left(2), Some(&6));
assert_eq!(tree.right(2), None); // No right child for node 2
assert!(tree.is_leaf(3));
assert!(!tree.is_leaf(0));
}
fn level_order_test() {
let tree = FlatTree::new(vec![1, 2, 3, 4, 5, 6, 7]);
let levels = tree.levels();
assert_eq!(levels.len(), 3);
assert_eq!(levels[0], vec![&1]);
assert_eq!(levels[1], vec![&2, &3]);
assert_eq!(levels[2], vec![&4, &5, &6, &7]);
}
fn heap_test() {
let mut tree = FlatTree::new(vec![3, 1, 4, 1, 5, 9, 2, 6]);
tree.heapify();
// Root should be max
assert_eq!(tree.get(0), Some(&9));
// Heap property: parent >= children
for i in 1..tree.data.len() {
let p = FlatTree::<i32>::parent(i);
assert!(
tree.data[p] >= tree.data[i],
"Heap violation: parent[{}]={:?} < child[{}]={:?}",
p,
tree.data[p],
i,
tree.data[i]
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
basic_tree();
}
#[test]
fn test_levels() {
level_order_test();
}
#[test]
fn test_heap() {
heap_test();
}
#[test]
fn test_depth() {
assert_eq!(FlatTree::new(vec![1]).depth(), 1);
assert_eq!(FlatTree::new(vec![1, 2, 3]).depth(), 2);
assert_eq!(FlatTree::new(vec![1, 2, 3, 4, 5, 6, 7]).depth(), 3);
}
#[test]
fn test_empty() {
let tree: FlatTree<i32> = FlatTree::new(vec![]);
assert_eq!(tree.depth(), 0);
assert_eq!(tree.get(0), None);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
basic_tree();
}
#[test]
fn test_levels() {
level_order_test();
}
#[test]
fn test_heap() {
heap_test();
}
#[test]
fn test_depth() {
assert_eq!(FlatTree::new(vec![1]).depth(), 1);
assert_eq!(FlatTree::new(vec![1, 2, 3]).depth(), 2);
assert_eq!(FlatTree::new(vec![1, 2, 3, 4, 5, 6, 7]).depth(), 3);
}
#[test]
fn test_empty() {
let tree: FlatTree<i32> = FlatTree::new(vec![]);
assert_eq!(tree.depth(), 0);
assert_eq!(tree.get(0), None);
}
}
Deep Comparison
Flat Binary Tree in Vec — Comparison
Core Insight
A flat tree represents a complete binary tree in an array: node i has children at 2i+1 and 2i+2, parent at (i-1)/2. No pointers, no allocations — just arithmetic. Both languages use this identically; the difference is just array vs Vec syntax.
OCaml Approach
tree.(i), tree.(2*i+1), etc.swap and sift_down with imperative loopsRust Approach
Vec<T> with index arithmeticdata.swap(i, j) built-insift_down with loop (no recursion needed)data.get(i) returns Option for safe accessComparison Table
| Feature | OCaml | Rust |
|---|---|---|
| Storage | 'a array | Vec<T> |
| Access | arr.(i) | vec[i] / vec.get(i) |
| Swap | Manual temp var | vec.swap(i, j) |
| Bounds check | Runtime exception | Panic or get() → Option |
| Heapify | Imperative loop | Imperative loop |
| Cache locality | Good (array) | Good (Vec) |
| Use case | Same: heaps, segment trees | Same: heaps, segment trees |
Exercises
heapify(&mut self) that turns any FlatTree into a max-heap in O(n) time using the sift-down procedure.in_order_traversal(&self) -> Vec<&T> that returns elements in sorted order from a heap-organized tree.SegmentTree on top of FlatTree<i64> that supports range-sum queries and point updates in O(log n).