文章目录
- 学习冒泡排序的可视化实现(一)
- 1.确保已经具备rust code environment;
- 2.创建新项目;
- 3.添加依赖项
- 4.初步展示图形化成果
- 5.优化图形化展示
- 实现思路:
- 6.整体优化
学习冒泡排序的可视化实现(一)
使用Rust实现冒泡排序的图形化,需要两个主要组件:Rust编程语言本身来实现冒泡排序算法,以及一个图形库来进行可视化。
使用piston_window库作为图形界面,因为它相对简单易用,适合入门级的项目。
1.确保已经具备rust code environment;
2.创建新项目;
cargo new rust_bubble_sort_visualization
cd rust_bubble_sort_visualization
3.添加依赖项
在项目文件中的cargo.toml文件中添加依赖项;
[dependencies]
piston_window = "0.120.0"
4.初步展示图形化成果
使用cargo run运行代码后可以看见显示的红色矩形图;
extern crate piston_window;
use piston_window::*;
fn bubble_sort(arr: &mut Vec<i32>) {
let mut n = arr.len();
let mut swapped = true;
while swapped {
swapped = false;
for i in 1..n {
if arr[i - 1] > arr[i] {
arr.swap(i - 1, i);
swapped = true;
}
}
n -= 1;
}
}
fn main() {
let mut window: PistonWindow = WindowSettings::new("Bubble Sort Visualization", [640, 480])
.exit_on_esc(true)
.build()
.unwrap();
let mut numbers = vec![10, 30, 20, 50, 40];
bubble_sort(&mut numbers);
while let Some(e) = window.next() {
window.draw_2d(&e, |c, g, _| {
clear([0.0, 0.0, 0.0, 1.0], g); // 清除屏幕
// 绘制排序后的数组
for (i, &val) in numbers.iter().enumerate() {
rectangle([1.0, 0.0, 0.0, 1.0], // 红色
[i as f64 * 100.0, 0.0, 50.0, val as f64], // 矩形位置和大小
c.transform, g);
}
});
}
}
5.优化图形化展示
-
实时可视化排序过程
- 将排序逻辑与绘图逻辑更紧密地结合起来。一种方法是,在每次交换元素后立即重新绘制界面。然而,由于Piston的事件循环是连续运行的,直接在排序函数中添加绘图逻辑可能会导致程序逻辑变得复杂。
-
添加用户交互元素
-
分步执行排序:通过全局状态控制排序的每一步执行。例如,您可以使用一个全局变量来记录当前排序的进度,并在每次窗口绘制事件中只执行一次或几次元素比较和交换。
-
处理输入事件:监听键盘或鼠标事件来控制排序的开始、暂停和重置。例如,您可以在按下特定键时开始排序,并在再次按下时暂停。
-
绘制控制按钮:在窗口中绘制表示开始、暂停和重置操作的按钮,并处理点击这些按钮的事件。
实现思路:
定义控制状态变量:在main函数中,定义几个变量来表示排序的状态(是否正在排序、是否暂停等)和一个变量来存储原始数组以方便重置。
let mut is_sorting = false;
let mut is_paused = false;
let original_numbers = numbers.clone();
处理键盘事件:在事件循环中,使用match语句来检测和响应键盘事件。例如,我们可以约定使用“S”键开始排序、“P”键暂停或恢复排序、“R”键重置数组到初始状态。
if let Some(Button::Keyboard(key)) = e.press_args() {
match key {
Key::S => {
is_sorting = true;
is_paused = false;
},
Key::P => {
if is_sorting {
is_paused = !is_paused;
}
},
Key::R => {
numbers = original_numbers.clone();
is_sorting = false;
is_paused = false;
n = total_numbers;
},
_ => {}
}
}
调整排序逻辑以响应状态变量:在绘制循环中,根据is_sorting和is_paused变量的值来决定是否执行排序步骤。如果is_sorting为true且is_paused为false,则执行一步排序操作。
if is_sorting && !is_paused && n > 1 {
let mut swapped = false;
for i in 1..n {
if numbers[i - 1] > numbers[i] {
numbers.swap(i - 1, i);
swapped = true;
break; // 在每次交换之后退出循环以重新绘制
}
}
if !swapped {
n -= 1;
}
if n <= 1 {
is_sorting = false; // 排序完成
}
}
完整代码:
extern crate piston_window;
use piston_window::*;
fn main() {
let mut window: PistonWindow = WindowSettings::new("Bubble Sort Visualization", [640, 480])
.exit_on_esc(true)
.build()
.unwrap();
let mut numbers = vec![10, 30, 20, 50, 40];
let original_numbers = numbers.clone(); // 保存原始数组以便重置
let mut is_sorting = false;
let mut is_paused = false;
let mut i = 0;
let mut n = numbers.len();
while let Some(e) = window.next() {
// 处理键盘事件
if let Some(Button::Keyboard(key)) = e.press_args() {
match key {
Key::S => { // 开始排序
is_sorting = true;
is_paused = false;
},
Key::P => { // 暂停/恢复排序
if is_sorting {
is_paused = !is_paused;
}
},
Key::R => { // 重置数组和状态
numbers = original_numbers.clone();
is_sorting = false;
is_paused = false;
i = 0;
n = numbers.len();
},
_ => {}
}
}
// 根据状态执行排序逻辑
if is_sorting && !is_paused && i < n - 1 {
for j in 0..n-i-1 {
if numbers[j] > numbers[j+1] {
numbers.swap(j, j+1);
break; // 在每次交换之后退出循环以重新绘制
}
}
i += 1;
} else if i >= n - 1 {
i = 0; // 重置i以循环排序过程
}
window.draw_2d(&e, |c, g, _| {
clear([0.0, 0.0, 0.0, 1.0], g); // 清除屏幕
// 绘制数组
for (i, &val) in numbers.iter().enumerate() {
rectangle([1.0, 0.0, 0.0, 1.0], // 红色
[i as f64 * 100.0, 480.0 - val as f64 * 10.0, 50.0, val as f64 * 10.0], // 矩形位置和大小
c.transform, g);
}
});
}
}
6.整体优化
将冒泡排序逻辑抽象成独立函数:这样做可以提高代码的可读性和可维护性。
增加枚举来管理程序状态:使用枚举替代布尔变量来控制排序状态,以便更清晰地管理不同的程序状态。
extern crate piston_window;
use piston_window::*;
enum SortState {
Idle,
Sorting,
Paused,
}
fn bubble_sort_step(numbers: &mut Vec<i32>, n: &mut usize) -> bool {
let mut swapped = false;
for i in 0..*n-1 {
if numbers[i] > numbers[i+1] {
numbers.swap(i, i+1);
swapped = true;
break; // 为了可视化,只进行一次交换
}
}
if !swapped {
*n = 0; // 如果没有发生交换,则排序完成
}
swapped
}
fn main() {
let mut window: PistonWindow = WindowSettings::new("Bubble Sort Visualization", [640, 480])
.exit_on_esc(true)
.build()
.unwrap();
let mut numbers = vec![10, 30, 20, 50, 40];
let original_numbers = numbers.clone();
let mut sort_state = SortState::Idle;
let mut n = numbers.len();
while let Some(e) = window.next() {
// 处理键盘事件
if let Some(Button::Keyboard(key)) = e.press_args() {
match key {
Key::S => sort_state = SortState::Sorting,
Key::P => match sort_state {
SortState::Sorting => sort_state = SortState::Paused,
SortState::Paused => sort_state = SortState::Sorting,
_ => {},
},
Key::R => {
numbers = original_numbers.clone();
sort_state = SortState::Idle;
n = numbers.len();
},
_ => {}
}
}
// 根据状态执行排序逻辑
match sort_state {
SortState::Sorting => {
if n > 0 {
bubble_sort_step(&mut numbers, &mut n);
} else {
sort_state = SortState::Idle; // 排序完成
}
},
_ => {}
}
window.draw_2d(&e, |c, g, _| {
clear([0.0, 0.0, 0.0, 1.0], g); // 清除屏幕
// 绘制数组
for (i, &val) in numbers.iter().enumerate() {
rectangle([1.0, 0.0, 0.0, 1.0], // 红色
[i as f64 * 100.0, 480.0 - val as f64 * 10.0, 50.0, val as f64 * 10.0], // 矩形位置和大小
c.transform, g);
}
});
}
}
增加缓步执行的功能,使查看变化更加清晰:
extern crate piston_window;
use piston_window::*;
use std::time::{Duration, Instant};
enum SortState {
Idle,
Sorting,
Paused,
}
fn bubble_sort_step(numbers: &mut Vec<i32>, n: &mut usize, last_swap_time: &mut Instant, delay: Duration) -> bool {
if last_swap_time.elapsed() >= delay {
let mut swapped = false;
for i in 0..*n-1 {
if numbers[i] > numbers[i+1] {
numbers.swap(i, i+1);
*last_swap_time = Instant::now(); // 更新交换时间
swapped = true;
break; // 为了可视化,只进行一次交换
}
}
if !swapped {
*n = 0; // 如果没有发生交换,则排序完成
}
swapped
} else {
false // 如果未达到延迟时间,不执行交换
}
}
fn main() {
let mut window: PistonWindow = WindowSettings::new("Bubble Sort Visualization", [640, 480])
.exit_on_esc(true)
.build()
.unwrap();
let mut numbers = vec![10, 30, 20, 50, 40];
let original_numbers = numbers.clone();
let mut sort_state = SortState::Idle;
let mut n = numbers.len();
let mut last_swap_time = Instant::now(); // 记录上次交换的时间
let delay = Duration::from_millis(500); // 设置延迟时间为500毫秒
while let Some(e) = window.next() {
if let Some(Button::Keyboard(key)) = e.press_args() {
match key {
Key::S => {
if matches!(sort_state, SortState::Idle) || matches!(sort_state, SortState::Paused) {
sort_state = SortState::Sorting;
}
},
Key::P => {
if matches!(sort_state, SortState::Sorting) {
sort_state = SortState::Paused;
}
},
Key::R => {