学习冒泡排序的可视化实现(一)

文章目录

  • 学习冒泡排序的可视化实现(一)
    • 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 => {
                   
上一篇:数据结构(三)------栈-总结


下一篇:asyncio&networkx&FuncAnimation学习--动态显示计算图的运行情况-二.代码