Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more sorting algorithms #4

Merged
merged 2 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<char>>();
let pre = Span::raw(line_chars[..*a].iter().collect::<String>());
let a_span = Span::raw(line_chars[*a..*a + 1].iter().collect::<String>())
let pre = Span::raw(line_chars[..a].iter().collect::<String>());
let a_span = Span::raw(line_chars[a..a + 1].iter().collect::<String>())
.fg(Color::LightCyan);
let mid = Span::raw(line_chars[*a + 1..*b].iter().collect::<String>());
let b_span = Span::raw(line_chars[*b..*b + 1].iter().collect::<String>())
let mid = Span::raw(line_chars[a + 1..b].iter().collect::<String>());
let b_span = Span::raw(line_chars[b..b + 1].iter().collect::<String>())
.fg(Color::LightCyan);
let last = Span::raw(line_chars[*b + 1..].iter().collect::<String>());
let last = Span::raw(line_chars[b + 1..].iter().collect::<String>());

line.spans = vec![pre, a_span, mid, b_span, last];
}
Expand All @@ -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::<Vec<char>>();
let pre = Span::raw(line_chars[..*a].iter().collect::<String>());
let a_span = Span::raw(line_chars[*a..*a + 1].iter().collect::<String>())
let pre = Span::raw(line_chars[..a].iter().collect::<String>());
let a_span = Span::raw(line_chars[a..a + 1].iter().collect::<String>())
.fg(Color::LightGreen);
let mid = Span::raw(line_chars[*a + 1..*b].iter().collect::<String>());
let b_span = Span::raw(line_chars[*b..*b + 1].iter().collect::<String>())
let mid = Span::raw(line_chars[a + 1..b].iter().collect::<String>());
let b_span = Span::raw(line_chars[b..b + 1].iter().collect::<String>())
.fg(Color::LightGreen);
let last = Span::raw(line_chars[*b + 1..].iter().collect::<String>());
let last = Span::raw(line_chars[b + 1..].iter().collect::<String>());

line.spans = vec![pre, a_span, mid, b_span, last];
}
Expand All @@ -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::<Vec<char>>();
let pre = Span::raw(line_chars[..*i].iter().collect::<String>());
let span = Span::raw(line_chars[*i..*i + 1].iter().collect::<String>())
let pre = Span::raw(line_chars[..i].iter().collect::<String>());
let span = Span::raw(line_chars[i..i + 1].iter().collect::<String>())
.fg(Color::LightYellow);
let last = Span::raw(line_chars[*i + 1..].iter().collect::<String>());
let last = Span::raw(line_chars[i + 1..].iter().collect::<String>());

line.spans = vec![pre, span, last];
}
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/sorting/bubble_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
51 changes: 51 additions & 0 deletions src/sorting/comb_sort.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
62 changes: 62 additions & 0 deletions src/sorting/heap_sort.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
35 changes: 35 additions & 0 deletions src/sorting/mod.rs
Original file line number Diff line number Diff line change
@@ -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<i32>);
Expand All @@ -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 {
Expand All @@ -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,
];
}

Expand All @@ -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"),
}
}
Expand Down
63 changes: 63 additions & 0 deletions src/sorting/quick_sort.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
45 changes: 45 additions & 0 deletions src/sorting/shell_sort.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
Loading