Tree Sort is a fascinating and intuitive sorting algorithm that uses the concept of binary search trees (BST) to sort elements efficiently. By inserting elements into a BST, the algorithm organizes data in such a way that an in-order traversal produces a sorted sequence. This makes Tree Sort both elegant and easy to understand for beginners, especially those interested in data structures and algorithms.
with hands-on learning.
get the skills and confidence to land your next move.
Tree Sort is particularly useful when you want a simple way to maintain a dynamic sorted collection of elements. It is commonly applied in scenarios where elements are being added over time, and you want to retrieve them in order without repeatedly sorting the entire dataset. For Rust learners, implementing Tree Sort helps solidify knowledge of recursive structures, pointers (via Box), and in-order traversals.
Program 1: Basic Tree Sort for Integers
This first program demonstrates a simple Tree Sort that sorts an array of integers using a binary search tree.
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
left: Option<Rc<RefCell<Node>>>,
right: Option<Rc<RefCell<Node>>>,
}
fn insert(root: &mut Option<Rc<RefCell<Node>>>, value: i32) {
match root {
Some(node) => {
if value < node.borrow().value {
insert(&mut node.borrow_mut().left, value);
} else {
insert(&mut node.borrow_mut().right, value);
}
}
None => {
*root = Some(Rc::new(RefCell::new(Node { value, left: None, right: None })));
}
}
}
fn inorder(root: &Option<Rc<RefCell<Node>>>, sorted: &mut Vec<i32>) {
if let Some(node) = root {
inorder(&node.borrow().left, sorted);
sorted.push(node.borrow().value);
inorder(&node.borrow().right, sorted);
}
}
fn tree_sort(arr: &[i32]) -> Vec<i32> {
let mut root: Option<Rc<RefCell<Node>>> = None;
for &value in arr.iter() {
insert(&mut root, value);
}
let mut sorted = Vec::new();
inorder(&root, &mut sorted);
sorted
}
fn main() {
let numbers = [5, 2, 9, 1, 5, 6];
println!("Original array: {:?}", numbers);
let sorted = tree_sort(&numbers);
println!("Sorted array: {:?}", sorted);
}This program works by building a binary search tree from the array elements, then performing an in-order traversal to produce a sorted list. Beginners can understand how trees naturally organize data for sorting without explicit comparison loops.
Program 2: Tree Sort Using Recursion Only
This version emphasizes a fully recursive approach, both for insertion and in-order traversal.
#[derive(Debug)]
struct Node {
value: i32,
left: Option<Box<Node>>,
right: Option<Box<Node>>,
}
fn insert_rec(root: Option<Box<Node>>, value: i32) -> Option<Box<Node>> {
match root {
Some(mut node) => {
if value < node.value {
node.left = insert_rec(node.left, value);
} else {
node.right = insert_rec(node.right, value);
}
Some(node)
}
None => Some(Box::new(Node { value, left: None, right: None })),
}
}
fn inorder_rec(root: &Option<Box<Node>>, sorted: &mut Vec<i32>) {
if let Some(node) = root {
inorder_rec(&node.left, sorted);
sorted.push(node.value);
inorder_rec(&node.right, sorted);
}
}
fn tree_sort_recursive(arr: &[i32]) -> Vec<i32> {
let mut root: Option<Box<Node>> = None;
for &value in arr.iter() {
root = insert_rec(root, value);
}
let mut sorted = Vec::new();
inorder_rec(&root, &mut sorted);
sorted
}
fn main() {
let numbers = [8, 3, 7, 1, 2];
println!("Original array: {:?}", numbers);
let sorted = tree_sort_recursive(&numbers);
println!("Sorted array: {:?}", sorted);
}This recursive approach makes it clear to beginners how each element finds its correct position in the tree and how in-order traversal ensures a sorted output.
Program 3: Tree Sort With Duplicate Handling
Here, the Tree Sort implementation ensures that duplicates are preserved, keeping the sort stable.
#[derive(Debug)]
struct Node {
value: i32,
count: usize,
left: Option<Box<Node>>,
right: Option<Box<Node>>,
}
fn insert_with_count(root: Option<Box<Node>>, value: i32) -> Option<Box<Node>> {
match root {
Some(mut node) => {
if value == node.value {
node.count += 1;
} else if value < node.value {
node.left = insert_with_count(node.left, value);
} else {
node.right = insert_with_count(node.right, value);
}
Some(node)
}
None => Some(Box::new(Node { value, count: 1, left: None, right: None })),
}
}
fn inorder_with_count(root: &Option<Box<Node>>, sorted: &mut Vec<i32>) {
if let Some(node) = root {
inorder_with_count(&node.left, sorted);
for _ in 0..node.count {
sorted.push(node.value);
}
inorder_with_count(&node.right, sorted);
}
}
fn tree_sort_with_duplicates(arr: &[i32]) -> Vec<i32> {
let mut root: Option<Box<Node>> = None;
for &value in arr.iter() {
root = insert_with_count(root, value);
}
let mut sorted = Vec::new();
inorder_with_count(&root, &mut sorted);
sorted
}
fn main() {
let numbers = [5, 3, 7, 3, 2, 5];
println!("Original array: {:?}", numbers);
let sorted = tree_sort_with_duplicates(&numbers);
println!("Sorted array: {:?}", sorted);
}This approach highlights to beginners how simple adjustments allow the algorithm to handle duplicates without losing their count.
Program 4: Tree Sort Using References
This program demonstrates using references with Rc and RefCell to allow multiple ownership and mutable access in Rust.
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
left: Option<Rc<RefCell<Node>>>,
right: Option<Rc<RefCell<Node>>>,
}
fn insert_ref(root: &mut Option<Rc<RefCell<Node>>>, value: i32) {
match root {
Some(node) => {
if value < node.borrow().value {
insert_ref(&mut node.borrow_mut().left, value);
} else {
insert_ref(&mut node.borrow_mut().right, value);
}
}
None => {
*root = Some(Rc::new(RefCell::new(Node { value, left: None, right: None })));
}
}
}
fn inorder_ref(root: &Option<Rc<RefCell<Node>>>, sorted: &mut Vec<i32>) {
if let Some(node) = root {
inorder_ref(&node.borrow().left, sorted);
sorted.push(node.borrow().value);
inorder_ref(&node.borrow().right, sorted);
}
}
fn main() {
let numbers = [4, 9, 1, 6];
let mut root: Option<Rc<RefCell<Node>>> = None;
for &num in numbers.iter() {
insert_ref(&mut root, num);
}
let mut sorted = Vec::new();
inorder_ref(&root, &mut sorted);
println!("Original array: {:?}", numbers);
println!("Sorted array: {:?}", sorted);
}This style demonstrates Rust’s ownership and borrowing rules, which are essential for safely managing dynamic tree structures.
Program 5: Tree Sort with Debug Output
This program adds debug prints to visualize how elements are inserted and traversed in Tree Sort.
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
left: Option<Box<Node>>,
right: Option<Box<Node>>,
}
fn insert_debug(root: &mut Option<Box<Node>>, value: i32) {
match root {
Some(node) => {
if value < node.value {
println!("Inserting {} to left of {}", value, node.value);
insert_debug(&mut node.left, value);
} else {
println!("Inserting {} to right of {}", value, node.value);
insert_debug(&mut node.right, value);
}
}
None => {
*root = Some(Box::new(Node { value, left: None, right: None }));
println!("Inserted {} as new node", value);
}
}
}
fn inorder_rec(root: &Option<Box<Node>>, sorted: &mut Vec<i32>) {
if let Some(node) = root {
inorder_rec(&node.left, sorted);
sorted.push(node.value);
inorder_rec(&node.right, sorted);
}
}
fn main() {
let numbers = [7, 2, 5, 3];
let mut root: Option<Box<Node>> = None;
for &num in numbers.iter() {
insert_debug(&mut root, num);
}
let mut sorted = Vec::new();
inorder_rec(&root, &mut sorted);
println!("Sorted array: {:?}", sorted);
}Debug output helps beginners visualize the decision-making process during insertion and understand why in-order traversal produces a sorted array.
Frequently Asked Questions (FAQ)
Q1: Is Tree Sort stable?
No, Tree Sort is not stable by default because equal elements may be placed in arbitrary order in the tree. However, it can be modified to preserve duplicates order.
Q2: What is the time complexity of Tree Sort?
Tree Sort has an average complexity of O(n log n) when using a balanced tree but can degrade to O(n²) for unbalanced trees.
Q3: Can Tree Sort handle duplicate elements?
Yes, by counting occurrences or using additional structures, duplicates can be handled properly.
Q4: Why use Tree Sort instead of quicksort?
Tree Sort naturally organizes data in a tree, which can be helpful for dynamic datasets where insertions and sorted order retrieval are needed.
Q5: Is Tree Sort suitable for large datasets?
It depends; Tree Sort works well with balanced trees, but for very large datasets, quicksort or mergesort is often more practical due to simpler memory and performance characteristics.
Conclusion
Tree Sort in Rust is an excellent way for beginners to explore sorting using binary search trees. Through multiple implementations, including recursion, handling duplicates, and using Rust’s ownership features, learners can deepen their understanding of both Rust and tree-based algorithms. Practicing Tree Sort reinforces concepts like recursion, dynamic memory management, and in-order traversal, building a strong foundation for more advanced data structures and algorithms.




