From cd4c439967ed9c02d549ff44b29b41e6db49fc95 Mon Sep 17 00:00:00 2001 From: Semih Buyukgungor Date: Sat, 10 Feb 2024 14:16:20 +0300 Subject: [PATCH] Add more sorting algorithms (#4) * add algorithms * adjust operation indexes --- src/main.rs | 30 +++++++++--------- src/sorting/bubble_sort.rs | 5 +++ src/sorting/comb_sort.rs | 51 ++++++++++++++++++++++++++++++ src/sorting/heap_sort.rs | 62 +++++++++++++++++++++++++++++++++++++ src/sorting/mod.rs | 35 +++++++++++++++++++++ src/sorting/quick_sort.rs | 63 ++++++++++++++++++++++++++++++++++++++ src/sorting/shell_sort.rs | 45 +++++++++++++++++++++++++++ 7 files changed, 276 insertions(+), 15 deletions(-) create mode 100644 src/sorting/comb_sort.rs create mode 100644 src/sorting/heap_sort.rs create mode 100644 src/sorting/quick_sort.rs create mode 100644 src/sorting/shell_sort.rs diff --git a/src/main.rs b/src/main.rs index 3db7fac..cfabdd8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,18 +136,18 @@ impl AlgorithmUI { result.push('\n'); } let mut text = Text::raw(result); - match operation { + match operation.adjusted() { Operation::Compare(a, b) => { for line in text.lines.iter_mut() { let line_content = line.spans[0].content.clone(); let line_chars = line_content.chars().into_iter().collect::>(); - let pre = Span::raw(line_chars[..*a].iter().collect::()); - let a_span = Span::raw(line_chars[*a..*a + 1].iter().collect::()) + let pre = Span::raw(line_chars[..a].iter().collect::()); + let a_span = Span::raw(line_chars[a..a + 1].iter().collect::()) .fg(Color::LightCyan); - let mid = Span::raw(line_chars[*a + 1..*b].iter().collect::()); - let b_span = Span::raw(line_chars[*b..*b + 1].iter().collect::()) + let mid = Span::raw(line_chars[a + 1..b].iter().collect::()); + let b_span = Span::raw(line_chars[b..b + 1].iter().collect::()) .fg(Color::LightCyan); - let last = Span::raw(line_chars[*b + 1..].iter().collect::()); + let last = Span::raw(line_chars[b + 1..].iter().collect::()); line.spans = vec![pre, a_span, mid, b_span, last]; } @@ -156,13 +156,13 @@ impl AlgorithmUI { for line in text.lines.iter_mut() { let line_content = line.spans[0].content.clone(); let line_chars = line_content.chars().into_iter().collect::>(); - let pre = Span::raw(line_chars[..*a].iter().collect::()); - let a_span = Span::raw(line_chars[*a..*a + 1].iter().collect::()) + let pre = Span::raw(line_chars[..a].iter().collect::()); + let a_span = Span::raw(line_chars[a..a + 1].iter().collect::()) .fg(Color::LightGreen); - let mid = Span::raw(line_chars[*a + 1..*b].iter().collect::()); - let b_span = Span::raw(line_chars[*b..*b + 1].iter().collect::()) + let mid = Span::raw(line_chars[a + 1..b].iter().collect::()); + let b_span = Span::raw(line_chars[b..b + 1].iter().collect::()) .fg(Color::LightGreen); - let last = Span::raw(line_chars[*b + 1..].iter().collect::()); + let last = Span::raw(line_chars[b + 1..].iter().collect::()); line.spans = vec![pre, a_span, mid, b_span, last]; } @@ -171,10 +171,10 @@ impl AlgorithmUI { for line in text.lines.iter_mut() { let line_content = line.spans[0].content.clone(); let line_chars = line_content.chars().into_iter().collect::>(); - let pre = Span::raw(line_chars[..*i].iter().collect::()); - let span = Span::raw(line_chars[*i..*i + 1].iter().collect::()) + let pre = Span::raw(line_chars[..i].iter().collect::()); + let span = Span::raw(line_chars[i..i + 1].iter().collect::()) .fg(Color::LightYellow); - let last = Span::raw(line_chars[*i + 1..].iter().collect::()); + let last = Span::raw(line_chars[i + 1..].iter().collect::()); line.spans = vec![pre, span, last]; } @@ -378,7 +378,7 @@ fn ui(frame: &mut Frame, app: &mut App) { if !algorithm.auto_next { let (step, operation) = algorithm.status.step_info(); - let info = format!("step: {}\n{}", step, operation); + let info = format!("step: {}\n{}", step, operation.adjusted()); let text_info = Text::from(info); let paragraph_info = Paragraph::new(text_info).alignment(Alignment::Left); let next_area = next_area_vertical(area, 2, 1); diff --git a/src/sorting/bubble_sort.rs b/src/sorting/bubble_sort.rs index c67ee5d..150219e 100644 --- a/src/sorting/bubble_sort.rs +++ b/src/sorting/bubble_sort.rs @@ -8,13 +8,18 @@ pub const NAME: &str = "bubble sort"; pub fn sort(nums: &mut [i32], ctx: &dyn AlgorithmContext) { let len = nums.len(); for i in 0..len { + let mut swapped = false; for j in 0..len - i - 1 { ctx.next(Compare(j, j + 1), nums.to_vec()); if nums[j] > nums[j + 1] { nums.swap(j, j + 1); ctx.next(Swap(j, j + 1), nums.to_vec()); + swapped = true; } } + if !swapped { + break; + } } ctx.next(Noop(), nums.to_vec()); } diff --git a/src/sorting/comb_sort.rs b/src/sorting/comb_sort.rs new file mode 100644 index 0000000..fb10317 --- /dev/null +++ b/src/sorting/comb_sort.rs @@ -0,0 +1,51 @@ +use super::{ + AlgorithmContext, + Operation::{Compare, Noop, Swap}, +}; + +pub const NAME: &str = "comb sort"; + +pub fn sort(nums: &mut [i32], ctx: &dyn AlgorithmContext) { + let n = nums.len(); + let mut gap = n; + let shrink_factor = 1.3; + let mut swapped = true; + + while gap > 1 || swapped { + gap = (gap as f64 / shrink_factor).floor() as usize; + if gap < 1 { + gap = 1; + } + + swapped = false; + + for i in 0..n - gap { + let j = i + gap; + ctx.next(Compare(i, j), nums.to_vec()); + if nums[i] > nums[j] { + nums.swap(i, j); + ctx.next(Swap(i, j), nums.to_vec()); + swapped = true; + } + } + } + + ctx.next(Noop(), nums.to_vec()); +} + +#[cfg(test)] +mod tests { + use crate::sorting::has_nums; + use crate::sorting::is_sorted; + use crate::sorting::NoopContext; + + use super::*; + + #[test] + fn test_sort() { + let nums = &mut [3, 5, 2, 8, 6, 9, 0, 1, 4, 7]; + sort(nums, &NoopContext); + assert!(is_sorted(nums)); + assert!(has_nums(nums)); + } +} diff --git a/src/sorting/heap_sort.rs b/src/sorting/heap_sort.rs new file mode 100644 index 0000000..87f3697 --- /dev/null +++ b/src/sorting/heap_sort.rs @@ -0,0 +1,62 @@ +use super::{ + AlgorithmContext, + Operation::{Compare, Noop, Swap}, +}; + +pub const NAME: &str = "heap sort"; + +pub fn sort(nums: &mut [i32], ctx: &dyn AlgorithmContext) { + let n = nums.len(); + for i in (0..n / 2).rev() { + heapify(nums, n, i, ctx); + } + for i in (1..n).rev() { + nums.swap(0, i); + ctx.next(Swap(0, i), nums.to_vec()); + heapify(nums, i, 0, ctx); + } + ctx.next(Noop(), nums.to_vec()); +} + +fn heapify(nums: &mut [i32], n: usize, i: usize, ctx: &dyn AlgorithmContext) { + let mut largest = i; + let left = 2 * i + 1; + let right = 2 * i + 2; + + if left < n { + ctx.next(Compare(left, largest), nums.to_vec()); + if nums[left] > nums[largest] { + largest = left; + } + } + + if right < n { + ctx.next(Compare(right, largest), nums.to_vec()); + if nums[right] > nums[largest] { + largest = right; + } + } + + if largest != i { + nums.swap(i, largest); + ctx.next(Swap(i, largest), nums.to_vec()); + heapify(nums, n, largest, ctx); + } +} + +#[cfg(test)] +mod tests { + use crate::sorting::has_nums; + use crate::sorting::is_sorted; + use crate::sorting::NoopContext; + + use super::*; + + #[test] + fn test_sort() { + let nums = &mut [3, 5, 2, 8, 6, 9, 0, 1, 4, 7]; + sort(nums, &NoopContext); + assert!(is_sorted(nums)); + assert!(has_nums(nums)); + } +} diff --git a/src/sorting/mod.rs b/src/sorting/mod.rs index f2efb9c..4c7dad6 100644 --- a/src/sorting/mod.rs +++ b/src/sorting/mod.rs @@ -1,9 +1,13 @@ use std::fmt::Display; pub mod bubble_sort; +pub mod comb_sort; +pub mod heap_sort; pub mod insertion_sort; pub mod merge_sort; +pub mod quick_sort; pub mod selection_sort; +pub mod shell_sort; pub trait AlgorithmContext { fn next(&self, operation: Operation, nums: Vec); @@ -17,6 +21,29 @@ pub enum Operation { Insert(usize), } +impl Operation { + pub fn adjusted(&self) -> Self { + return match *self { + Self::Compare(a, b) => { + if a > b { + Self::Compare(b, a) + } else { + Self::Compare(a, b) + } + } + Self::Swap(a, b) => { + if a > b { + Self::Swap(b, a) + } else { + Self::Swap(a, b) + } + } + Self::Insert(i) => return Self::Insert(i), + Self::Noop() => return Self::Noop(), + }; + } +} + impl Display for Operation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -40,6 +67,10 @@ pub fn get_algorithms() -> Vec<&'static str> { selection_sort::NAME, insertion_sort::NAME, merge_sort::NAME, + shell_sort::NAME, + heap_sort::NAME, + quick_sort::NAME, + comb_sort::NAME, ]; } @@ -49,6 +80,10 @@ pub fn get_algorithm_func<'a>(s: &str) -> impl FnOnce(&mut [i32], &dyn Algorithm selection_sort::NAME => selection_sort::sort, insertion_sort::NAME => insertion_sort::sort, merge_sort::NAME => merge_sort::sort, + shell_sort::NAME => shell_sort::sort, + heap_sort::NAME => heap_sort::sort, + quick_sort::NAME => quick_sort::sort, + comb_sort::NAME => comb_sort::sort, _ => panic!("algorithm not found"), } } diff --git a/src/sorting/quick_sort.rs b/src/sorting/quick_sort.rs new file mode 100644 index 0000000..00c6208 --- /dev/null +++ b/src/sorting/quick_sort.rs @@ -0,0 +1,63 @@ +use super::{ + AlgorithmContext, + Operation::{Compare, Noop, Swap}, +}; + +pub const NAME: &str = "quick sort"; + +pub fn sort(nums: &mut [i32], ctx: &dyn AlgorithmContext) { + quick_sort_recursive(nums, 0, nums.len() - 1, ctx); + ctx.next(Noop(), nums.to_vec()); +} + +fn quick_sort_recursive(nums: &mut [i32], low: usize, high: usize, ctx: &dyn AlgorithmContext) { + if low < high { + let pivot_index = partition(nums, low, high, ctx); + + if pivot_index > 0 { + quick_sort_recursive(nums, low, pivot_index - 1, ctx); + } + + quick_sort_recursive(nums, pivot_index + 1, high, ctx); + } +} + +fn partition(nums: &mut [i32], low: usize, high: usize, ctx: &dyn AlgorithmContext) -> usize { + let pivot = nums[high]; + let mut i = low; + + for j in low..high { + ctx.next(Compare(j, high), nums.to_vec()); + if nums[j] <= pivot { + if i != j { + nums.swap(i, j); + ctx.next(Swap(i, j), nums.to_vec()); + } + i += 1; + } + } + + if i != high { + nums.swap(i, high); + ctx.next(Swap(i, high), nums.to_vec()); + } + + return i; +} + +#[cfg(test)] +mod tests { + use crate::sorting::has_nums; + use crate::sorting::is_sorted; + use crate::sorting::NoopContext; + + use super::*; + + #[test] + fn test_sort() { + let nums = &mut [3, 5, 2, 8, 6, 9, 0, 1, 4, 7]; + sort(nums, &NoopContext); + assert!(is_sorted(nums)); + assert!(has_nums(nums)); + } +} diff --git a/src/sorting/shell_sort.rs b/src/sorting/shell_sort.rs new file mode 100644 index 0000000..dc46e1c --- /dev/null +++ b/src/sorting/shell_sort.rs @@ -0,0 +1,45 @@ +use super::{ + AlgorithmContext, + Operation::{Compare, Noop, Swap}, +}; + +pub const NAME: &str = "shell sort"; + +pub fn sort(nums: &mut [i32], ctx: &dyn AlgorithmContext) { + let n = nums.len(); + let mut gap = n / 2; + while gap > 0 { + for i in gap..n { + let mut j = i; + while j >= gap { + ctx.next(Compare(j - gap, j), nums.to_vec()); + if nums[j - gap] > nums[j] { + nums.swap(j - gap, j); + ctx.next(Swap(j - gap, j), nums.to_vec()); + j -= gap; + } else { + break; + } + } + } + gap /= 2; + } + ctx.next(Noop(), nums.to_vec()); +} + +#[cfg(test)] +mod tests { + use crate::sorting::has_nums; + use crate::sorting::is_sorted; + use crate::sorting::NoopContext; + + use super::*; + + #[test] + fn test_sort() { + let nums = &mut [3, 5, 2, 8, 6, 9, 0, 1, 4, 7]; + sort(nums, &NoopContext); + assert!(is_sorted(nums)); + assert!(has_nums(nums)); + } +}