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

本文介绍了如何使用Rust语言结合Piston_window库实现冒泡排序的图形化可视化过程,包括项目初始化、添加依赖、基本展示、优化实时交互与动画效果,以及整体代码结构的改进和性能提升。
摘要由CSDN通过智能技术生成

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

使用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 => {
                    numbers = original_numbers.clone();
                    sort_state = SortState::Idle;
                    n = numbers.len();
                    last_swap_time = Instant::now(); // 重置交换时间
                },
                _ => {}
            }
        }

        if matches!(sort_state, SortState::Sorting) {
            bubble_sort_step(&mut numbers, &mut n, &mut last_swap_time, delay);
        }

        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);
            }
        });
    }
}

支持大数据量的同时将缓步执行变为可选项,支持按键S开始排序,按键R重置,按键space进行缓步执行,按键p暂停:

extern crate piston_window;
use piston_window::*;

use std::time::{Duration, Instant};

#[derive(PartialEq)] // 为了能够使用 == 操作符
enum SortState {
    Idle,
    Sorting,
    Paused,
    Step,
}

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", [1280, 720])
        .exit_on_esc(true)
        .build()
        .unwrap();

    let original_numbers = vec![10, 5, 3, 8, 2, 6, 4, 7, 9, 1, 55, 23, 12, 34, 11, 22, 33, 44]; // 初始数列
    let mut numbers = original_numbers.clone(); // 数列的工作副本
    let mut n = numbers.len();
    let mut last_swap_time = Instant::now();
    let delay = Duration::from_millis(10);
    let mut sort_state = SortState::Idle;

    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::Space => { // 处理空格键
                    if matches!(sort_state, SortState::Sorting) {
                        // 如果在排序中,按下空格键暂停排序
                        sort_state = SortState::Paused;
                    } else if matches!(sort_state, SortState::Paused) {
                        // 如果已暂停,按下空格键执行单步排序
                        if bubble_sort_step(&mut numbers, &mut n, &mut last_swap_time, delay) {
                            // 如果执行了交换操作,则继续保持暂停状态等待下一步
                            sort_state = SortState::Paused;
                        } else {
                            // 如果没有执行交换(排序完成),则切换到Idle状态
                            sort_state = SortState::Idle;
                        }
                    }
                },
                Key::R => {
                    numbers = original_numbers.clone();
                    sort_state = SortState::Idle;
                    n = numbers.len();
                    last_swap_time = Instant::now(); // 重置交换时间
                },
                _ => {}
            }
        }


        // 根据当前状态进行排序或暂停
        if sort_state == SortState::Sorting {
            if !bubble_sort_step(&mut numbers, &mut n, &mut last_swap_time, delay) && n == 0 {
                sort_state = SortState::Idle; // 排序完成,回到空闲状态
            }
        }

        window.draw_2d(&e, |c, g, _| {
            clear([0.0, 0.0, 0.0, 1.0], g); // 清除背景

            let max_number = *numbers.iter().max().unwrap() as f64;
            let rect_width = (1280.0 / numbers.len() as f64).floor();
            for (i, &number) in numbers.iter().enumerate() {
                let rect_height = (720.0 * (number as f64 / max_number)).floor(); // 根据数值大小动态调整高度
                rectangle([1.0, 0.0, 0.0, 1.0], // 红色矩形
                          [i as f64 * rect_width, 720.0 - rect_height, rect_width, rect_height], // 矩形位置和大小
                          c.transform,
                          g);
            }
        });
    }
}

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C语言实现冒泡排序的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #include <windows.h> #define MAX_NUM 20 // 数组最大长度 #define MAX_VALUE 100 // 数组元素最大值 #define SLEEP_TIME 500 // 可延迟时间(毫秒) void printArray(int a[], int n); // 打印数组 void bubbleSort(int a[], int n); // 冒泡排序 int main() { int a[MAX_NUM]; int n = MAX_NUM; // 随机生成数组 srand((unsigned)time(NULL)); for (int i = 0; i < n; i++) { a[i] = rand() % (MAX_VALUE + 1); } // 打印初始数组 printf("Initial array:\n"); printArray(a, n); // 冒泡排序 bubbleSort(a, n); return 0; } // 打印数组 void printArray(int a[], int n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } // 冒泡排序 void bubbleSort(int a[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (a[j] > a[j + 1]) { // 交换相邻元素 int temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; // 可交换过程 printf("Sorting: "); for (int k = 0; k < n; k++) { if (k == j) { printf("\033[1;31m"); // 红色高亮 } else if (k == j + 1) { printf("\033[1;32m"); // 绿色高亮 } printf("%d ", a[k]); printf("\033[0m"); // 恢复颜色 } Sleep(SLEEP_TIME); printf("\n"); } } } // 打印排序后的数组 printf("Sorted array:\n"); printArray(a, n); } ``` 在上面的代码中,我们使用了ANSI转义序列来实现终端高亮显示。具体地,我们使用`\033[1;31m`来设置红色高亮,`\033[1;32m`来设置绿色高亮,`\033[0m`来恢复颜色。 当然,这种可方式也可以用于其他排序算法的演示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值