数据结构-排序实验

题目

内部排序算法比较

对7种常用的内部排序算法进行比较:直接插入排序、希尔排序、冒泡排序、快速排序、简单选择排序、堆排序、二路归并排序。
待排序表的表长不少于100;其中的数据要用伪随机数产生程序产生;至少要用5组不同的输入数据作比较;比较的指标为关键字参加的比较次数和数据元素的移动次数(关键字交换计为3次移动)。
对不同的输入表长做试验,观察两个指标相对于表长的变化关系,也可以对稳定性做验证。

统计成绩

给出n个学生的m门考试的成绩表,每个学生的信息由学号、姓名以及各科成绩组成。对学生的考试成绩进行有关统计,并打印统计表。
①按总分由高到低的顺序,打印出名次表,分数相同的为同一名次;
②按名次打印出每个学生的学号、姓名、总分以及各科成绩。

一

一些经验,rust对于类型要求很严格,所以用作索引的一律用usize,因为for循环里的i默认是,统计数据用u64,如果u32溢出程序会直接panic

use std::mem::swap;
use tracing::info;
use tracing_subscriber::filter::LevelFilter;

pub struct  Benchmark{
    compare_time : u64,
    move_time : u64,
}

struct Sort{
    bench : Benchmark,
}

impl Sort{

    pub fn new() -> Sort{
        Sort{
            bench : Benchmark{
                compare_time : 0,
                move_time : 0,
            }
        }
    }

    pub fn compare(&mut self, num1 : i32, num2 : i32) -> bool{
        self.bench.compare_time += 1;
        num1 > num2
    }

    pub fn insert_sort(&mut self, nums : &mut Vec<i32>){
        let mut temp = 0;
        for i in 1..nums.len(){
            if self.compare(nums[i-1], nums[i]) { //如果小于,直接跳过
                temp = nums[i];
                let mut j = i;

                while j > 0 && !self.compare(temp, nums[j-1]){ // 与第j-1个元素相比较
                    nums[j] = nums[j-1];
                    self.bench.move_time += 1;
                    j -= 1;
                }
                nums[j] = temp;
                self.bench.move_time +=1;
            }
        }
    }

    pub fn shell_sort(&mut self, nums : &mut Vec<i32>) {
        let dlta = vec![8,4,2,1];
        for k in 0..dlta.len(){
            self.shell_insert(nums, dlta[k]);
        }
    }

    fn shell_insert(&mut self, nums : &mut Vec<i32>, d : u32){
        // d为步长
        let dd = d as usize;
        // 分成d次插入排序
        for i in dd..nums.len(){
            if self.compare(nums[i - dd],nums[i]){
                let temp = nums[i];
                let mut j = i;

                while j >= dd && self.compare(nums[j - dd], temp) {
                    nums[j] = nums[j - dd];
                    self.bench.move_time += 1;
                    j -= dd;
                }

                nums[j] = temp;
                self.bench.move_time += 1;
            }
        }
    }

    pub fn bubble_sort(&mut self, nums : &mut Vec<i32>){
        let mut change = true;
        let n = nums.len();
        for i in 0..n{
            if change == false{
                return;
            }
            change = false;
            for j in 1..n - i{
                if self.compare(nums[j-1], nums[j]){
                    // 交换,记录
                    nums.swap(j-1, j);
                    self.bench.move_time += 3;
                    change = true;
                }
            }
        }
    }

    pub fn quick_sort(&mut self, nums : &mut Vec<i32>, low: usize, high: usize){
        if high > low {
            let pi = self.partition(nums, low, high);

            if pi > low { // 避免pi = 0发生溢出的情况
                self.quick_sort(nums, low, pi - 1);
            }

            self.quick_sort(nums, pi + 1, high);
        }
    }

    fn partition(&mut self, nums : &mut Vec<i32>, low: usize, high: usize) -> usize{
        let pivot = nums[high as usize];
        let mut index : usize = low as usize;
        for j in low..high {
            if self.compare(pivot, nums[j]){
                index += 1;
                nums.swap(index -1,j);
                self.bench.move_time += 3;

            }
        }
        nums.swap(index,high as usize);
        index
    }

    pub fn select_sort(&mut self, nums : &mut Vec<i32>){
        let t = nums.len();
        for i in 0..t-1{
            let target = t - 1 - i;
            let k = self.find_max(nums, t-i);
            if k != target{
                nums.swap(k,target);
                self.bench.move_time += 3;
            }
        }
    }

    fn find_max(&mut self, nums : &mut Vec<i32>, len:usize) -> usize{
        let mut max = nums[0];
        let mut max_index = 0;
        for i in 1..len{
            if self.compare(nums[i],max){
                max_index = i;
                max = nums[i];
            }
        }
        max_index
    }

    pub fn heap_sort(&mut self, nums : &mut Vec<i32>){
        let len = nums.len();
        let last_parent = (len / 2) - 1;

        for i in (0..=last_parent).rev(){
            self.sift_down(nums, i, len - 1);
        }

        for i in (1..=len - 1).rev(){
            nums.swap(0, i);
            self.bench.move_time += 3;

            self.sift_down(nums, 0, i-1);

        }
    }

    fn sift_down(&mut self, nums : &mut Vec<i32>, start : usize, end : usize){
        let mut parent = start;
        let mut child = parent * 2 + 1;
        while child <= end{
            if child + 1 <= end && self.compare(nums[child + 1], nums[child]){
                child += 1;
            }
            if self.compare(nums[parent],nums[child] ) {
                return;
            }else{
                nums.swap(child, parent);
                self.bench.move_time += 3;
                parent = child;
                child = parent * 2 + 1;
            }
        }
    }

    pub fn merge_sort(&mut self, nums : &mut Vec<i32>, left : usize, right : usize){
        if left < right{
            let mid = left + (right - left) / 2;
            self.merge_sort(nums, left, mid);
            self.merge_sort(nums, mid + 1, right);
            self.merge(nums, left,mid, right);
        }
    }

    fn merge(&mut self, nums : &mut Vec<i32>, left : usize,mid : usize, right : usize){
        let l1 = mid - left + 1;
        let l2 = right - mid;

        let v1 = nums[left..left + l1].to_vec();
        let v2 = nums[mid + 1..mid + 1 + l2].to_vec();
        self.bench.move_time += (l1 + l2) as u64;

        let mut i = 0;
        let mut j = 0;
        let mut k = left;

        while i<l1 && j<l2{
            if self.compare(v1[i], v2[j]){
                nums[k] = v2[j];
                self.bench.move_time += 1;
                j += 1;
            }else {
                nums[k] = v1[i];
                self.bench.move_time += 1;
                i += 1;
            }
            k += 1;
        }

        while i < l1{
            nums[k] = v1[i];
            self.bench.move_time += 1;
            i += 1;
            k += 1;
        }

        while i < l2{
            nums[k] = v2[j];
            self.bench.move_time += 1;
            j += 1;
            k += 1;
        }

    }
}


#[cfg(test)]
mod test{
    use crate::Sort;

    #[test]
    fn test_sort() {
        use rand::Rng;
        use tracing::info;
        use tracing_subscriber::filter::LevelFilter;

        let _ = tracing_subscriber::fmt()
            .with_max_level(LevelFilter::INFO)
            .with_test_writer()
            .try_init();

        let mut rng = rand::rng();

        // 定义5组测试数据的长度
        let test_sizes = [100, 500, 1000, 2000, 5000, 10000, 100000];

        for (round, &len) in test_sizes.iter().enumerate() {
            info!("=== 第 {} 组测试 | 数据量: {} ===", round + 1, len);

            // 生成随机数据
            let origin: Vec<i32> = (0..len).map(|_| rng.random_range(-10000..10000)).collect();
            let sorted_std = {
                let mut v = origin.clone();
                v.sort();
                v
            };

            // 定义算法列表:(名称, 执行闭包)
            // 使用 Box<dyn Fn...> 来统一不同函数签名的调用
            let algorithms: Vec<(&str, Box<dyn Fn(&mut Sort, &mut Vec<i32>)>)> = vec![
                ("插入排序", Box::new(|s, v| s.insert_sort(v))),
                ("希尔排序", Box::new(|s, v| s.shell_sort(v))),
                ("冒泡排序", Box::new(|s, v| s.bubble_sort(v))),
                ("选择排序", Box::new(|s, v| s.select_sort(v))),
                ("堆排序  ", Box::new(|s, v| s.heap_sort(v))),
                ("快速排序", Box::new(|s, v| {
                    let l = v.len();
                    if l > 0 { s.quick_sort(v, 0, l - 1); }
                })),
                ("归并排序", Box::new(|s, v| {
                    let l = v.len();
                    if l > 0 { s.merge_sort(v, 0, l - 1); }
                })),
            ];

            for (name, func) in algorithms {
                let mut data = origin.clone();
                let mut sort_instance = Sort::new(); // 每次新建实例

                // 执行排序
                func(&mut sort_instance, &mut data);

                // 验证结果 (使用标准库排序结果作为基准)
                assert_eq!(data, sorted_std, "{} 结果不正确!", name);

                // 打印结果 (格式化对齐)
                info!(
                    "算法: {} | 比较: {:>9} | 移动: {:>9}",
                    name,
                    sort_instance.bench.compare_time,
                    sort_instance.bench.move_time
                );
            }
        }
    }
    /*
    fn verify_sort(num : Vec<i32>, sorted : Vec<i32>) -> Result<(), String>{
        if num.len() != sorted.len() {
            return Err("长度不相等".to_string());
        }

        // 检查排序后的数组是否有序
        for i in 1..sorted.len() {
            if sorted[i] < sorted[i - 1] {
                return Err("排序后数组存在逆序".to_string());
            }
        }

        // 检查两个数组是否包含相同的元素(顺序可以不同)
        let mut num_sorted = num.clone();
        num_sorted.sort();

        if num_sorted == sorted {
           Ok(())
        }else{
            Err("数组不相等".to_string())
        }
    }
    */
}

运行结果:

running 1 test
2025-12-22T05:01:19.791577Z  INFO ex7::test: === 第 1 组测试 | 数据量: 100 ===
2025-12-22T05:01:19.792588Z  INFO ex7::test: 算法: 插入排序 | 比较:    2702 | 移动:    2609
2025-12-22T05:01:19.793373Z  INFO ex7::test: 算法: 希尔排序 | 比较:    1145 | 移动:     781
2025-12-22T05:01:19.794736Z  INFO ex7::test: 算法: 冒泡排序 | 比较:    4905 | 移动:    7536
2025-12-22T05:01:19.796295Z  INFO ex7::test: 算法: 选择排序 | 比较:    4950 | 移动:     282
2025-12-22T05:01:19.797076Z  INFO ex7::test: 算法: 堆排序   | 比较:    1027 | 移动:    1746
2025-12-22T05:01:19.797772Z  INFO ex7::test: 算法: 快速排序 | 比较:     727 | 移动:    1095
2025-12-22T05:01:19.798186Z  INFO ex7::test: 算法: 归并排序 | 比较:     549 | 移动:    1292
2025-12-22T05:01:19.798396Z  INFO ex7::test: === 第 2 组测试 | 数据量: 500 ===
2025-12-22T05:01:19.803050Z  INFO ex7::test: 算法: 插入排序 | 比较:   62463 | 移动:   61970
2025-12-22T05:01:19.804701Z  INFO ex7::test: 算法: 希尔排序 | 比较:   13345 | 移动:   11392
2025-12-22T05:01:19.818697Z  INFO ex7::test: 算法: 冒泡排序 | 比较:  124372 | 移动:  184407
2025-12-22T05:01:19.821945Z  INFO ex7::test: 算法: 选择排序 | 比较:  124750 | 移动:    1485
2025-12-22T05:01:19.822856Z  INFO ex7::test: 算法: 堆排序   | 比较:    7440 | 移动:   12117
2025-12-22T05:01:19.823920Z  INFO ex7::test: 算法: 快速排序 | 比较:    5113 | 移动:    9120
2025-12-22T05:01:19.824957Z  INFO ex7::test: 算法: 归并排序 | 比较:    3856 | 移动:    8694
2025-12-22T05:01:19.825448Z  INFO ex7::test: === 第 3 组测试 | 数据量: 1000 ===
2025-12-22T05:01:19.839718Z  INFO ex7::test: 算法: 插入排序 | 比较:  256070 | 移动:  255078
2025-12-22T05:01:19.843516Z  INFO ex7::test: 算法: 希尔排序 | 比较:   54399 | 移动:   50467
2025-12-22T05:01:19.879929Z  INFO ex7::test: 算法: 冒泡排序 | 比较:  498905 | 移动:  762174
2025-12-22T05:01:19.891824Z  INFO ex7::test: 算法: 选择排序 | 比较:  499500 | 移动:    2982
2025-12-22T05:01:19.894710Z  INFO ex7::test: 算法: 堆排序   | 比较:   16804 | 移动:   27147
2025-12-22T05:01:19.895870Z  INFO ex7::test: 算法: 快速排序 | 比较:   10949 | 移动:   14496
2025-12-22T05:01:19.897454Z  INFO ex7::test: 算法: 归并排序 | 比较:    8709 | 移动:   19296
2025-12-22T05:01:19.898132Z  INFO ex7::test: === 第 4 组测试 | 数据量: 2000 ===
2025-12-22T05:01:19.943426Z  INFO ex7::test: 算法: 插入排序 | 比较:  970591 | 移动:  968597
2025-12-22T05:01:19.949645Z  INFO ex7::test: 算法: 希尔排序 | 比较:  156710 | 移动:  148767
2025-12-22T05:01:20.075452Z  INFO ex7::test: 算法: 冒泡排序 | 比较: 1998790 | 移动: 2899533
2025-12-22T05:01:20.126713Z  INFO ex7::test: 算法: 选择排序 | 比较: 1999000 | 移动:    5973
2025-12-22T05:01:20.129949Z  INFO ex7::test: 算法: 堆排序   | 比较:   37755 | 移动:   60588
2025-12-22T05:01:20.132023Z  INFO ex7::test: 算法: 快速排序 | 比较:   25649 | 移动:   38115
2025-12-22T05:01:20.134511Z  INFO ex7::test: 算法: 归并排序 | 比较:   19412 | 移动:   42698
2025-12-22T05:01:20.134763Z  INFO ex7::test: === 第 5 组测试 | 数据量: 5000 ===
2025-12-22T05:01:20.396978Z  INFO ex7::test: 算法: 插入排序 | 比较: 6203283 | 移动: 6198287
2025-12-22T05:01:20.444421Z  INFO ex7::test: 算法: 希尔排序 | 比较:  918591 | 移动:  898657
2025-12-22T05:01:21.271658Z  INFO ex7::test: 算法: 冒泡排序 | 比较: 12496639 | 移动: 18577998
2025-12-22T05:01:21.593931Z  INFO ex7::test: 算法: 选择排序 | 比较: 12497500 | 移动:   14982
2025-12-22T05:01:21.600998Z  INFO ex7::test: 算法: 堆排序   | 比较:  107656 | 移动:  171342
2025-12-22T05:01:21.605011Z  INFO ex7::test: 算法: 快速排序 | 比较:   68277 | 移动:  100923
2025-12-22T05:01:21.610876Z  INFO ex7::test: 算法: 归并排序 | 比较:   55199 | 移动:  120893
2025-12-22T05:01:21.611331Z  INFO ex7::test: === 第 6 组测试 | 数据量: 10000 ===
2025-12-22T05:01:22.675283Z  INFO ex7::test: 算法: 插入排序 | 比较: 24842700 | 移动: 24832712
2025-12-22T05:01:22.817031Z  INFO ex7::test: 算法: 希尔排序 | 比较: 3423814 | 移动: 3383885
2025-12-22T05:01:26.095848Z  INFO ex7::test: 算法: 冒泡排序 | 比较: 49978529 | 移动: 74460552
2025-12-22T05:01:27.326666Z  INFO ex7::test: 算法: 选择排序 | 比较: 49995000 | 移动:   29973
2025-12-22T05:01:27.342937Z  INFO ex7::test: 算法: 堆排序   | 比较:  235350 | 移动:  372729
2025-12-22T05:01:27.352162Z  INFO ex7::test: 算法: 快速排序 | 比较:  146882 | 移动:  225297
2025-12-22T05:01:27.364114Z  INFO ex7::test: 算法: 归并排序 | 比较:  120507 | 移动:  261739
2025-12-22T05:01:27.364474Z  INFO ex7::test: === 第 7 组测试 | 数据量: 100000 ===
test test::test_sort has been running for over 60 seconds
2025-12-22T05:03:33.557852Z  INFO ex7::test: 算法: 插入排序 | 比较: 2489615146 | 移动: 2489515158
2025-12-22T05:03:47.811176Z  INFO ex7::test: 算法: 希尔排序 | 比较: 323937261 | 移动: 323537347
2025-12-22T05:11:05.291893Z  INFO ex7::test: 算法: 冒泡排序 | 比较: 4999918875 | 移动: 7467497442
2025-12-22T05:13:36.252963Z  INFO ex7::test: 算法: 选择排序 | 比较: 4999950000 | 移动:  299970
2025-12-22T05:13:36.433015Z  INFO ex7::test: 算法: 堆排序   | 比较: 3019688 | 移动: 4725597
2025-12-22T05:13:36.563771Z  INFO ex7::test: 算法: 快速排序 | 比较: 1994351 | 移动: 2669799
2025-12-22T05:13:36.738450Z  INFO ex7::test: 算法: 归并排序 | 比较: 1536095 | 移动: 3282766
test test::test_sort ... ok
test test::test_sort ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 736.95s

     Running unittests src\main.rs (target\debug\deps\ex7-c62cfa010482da9d.exe)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests ex7

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

对于最后一组,十万数据的排序测试,冒泡排序跑了7分多钟,居然这么慢,插入排序和选择排序跑了两分多钟

shell排序因为步长很短,效果并不好,但是可以优化

二

实现

一道排序算法模板题,但是没有标准库可用,同时要求输出时附带名次且同分同名

既然测试的结果快排最好,那接下来就在快排的基础上改,首先把它变成一个泛型函数:

pub fn quick_sort <T>(&mut self, nums : &mut [T], low: usize, high: usize)
where
    T : PartialOrd + Clone,
{
    if high > low {
        let pi = self.partition(nums, low, high);

        if pi > low { // 避免pi = 0发生溢出的情况
            self.quick_sort(nums, low, pi - 1);
        }

        self.quick_sort(nums, pi + 1, high);
    }
}

fn partition <T>(&mut self, nums : &mut [T], low: usize, high: usize) -> usize
where
    T : PartialOrd + Clone,
{
    let pivot = nums[high].clone();
    let mut index = low ;
    for j in low..high {
        if self.compare(pivot.clone(), nums[j].clone()){
            index += 1;
            nums.swap(index -1,j);
            self.bench.move_time += 3;

        }
    }
    nums.swap(index,high);
    index
}

pub fn compare<T: PartialOrd>(&mut self, num1 : T, num2 : T) -> bool{
    self.bench.compare_time += 1;
    num1 > num2
}

compare函数的比较对象并没有写成引用,这导致每次调用compare会移交所有权,而循环在继续,所以不得不.clone(),但这又会造成不必要的开销。i32类型时没有所有权问题是因为i32具有copy特征,其表示按位复制,当实现了copy特征后传参就会变成按位复制而非移动所有权。如果我传入一个指针,它会在函数结束被free掉,在自己的生命周期结束再被free,显然rust不会允许这种事情发生。涉及堆的、文件句柄、连接等的数据都无法实现copy特征

按理说compare的参数应当是引用,这是最标准的写法,这里为避免大面积修改clone了事了

接下来是Student结构体的实现,单独划分出一个字段记录名次,同时实现必要的特征

#[derive(Debug, Clone)]
pub struct Student{
    id : u64,
    name : String,
    math : i32,
    chinese : i32,
    english : i32,
    order : usize,
}


impl Student{
    pub fn new(id : u64, name : String, math : i32, chinese : i32, english : i32) -> Self{
        Self{
            id,
            name,
            math,
            chinese,
            english,
            order : 0,
        }
    }
}

impl fmt::Display for Student{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let total = self.math + self.chinese + self.english;
        write!(f,"学号:{}, 姓名:{} 总分:{}, 语文:{} 数学:{}英语:{}",self.id,self.name,total,self.chinese,self.math,self.english)
    }
}

impl PartialEq for Student{
    fn eq(&self, other: &Self) -> bool{
        let t1 = self.math + self.chinese + self.english;
        let t2 = other.math + other.chinese + other.english;
        t1 == t2
    }
}

impl PartialOrd for Student{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        let t1 = self.math + self.chinese + self.english;
        let t2 = other.math + other.chinese + other.english;

        t1.partial_cmp(&t2)
    }
}

然后是核心的排序输出:

pub fn sort(mut s : Vec<Student>){
    let mut sort_instance = Sort::new();
    let len = s.len();
    sort_instance.quick_sort(&mut s, 0, len - 1);
    let mut current_score = 0;
    let mut current_order = 0;
    for i in (0..len).rev(){
        let order = len - 1 - i;
        let score = s[i].chinese + s[i].math + s[i].english;

        let mut index = order;
        if current_score == score {
            index = current_order;
        }else{
            current_order = index;
            current_score = score;
        }
        info!("第{}名,{}",index + 1,s[i]);
    }
}

这个名次处理感觉实现得笨笨的;总分应该单独维护一个字段的。倒序遍历列表输出,测试:

#[test]
fn test_2(){
    let _ = tracing_subscriber::fmt()
        .with_max_level(LevelFilter::INFO)
        .with_test_writer()
        .try_init();

    let mut data = generate_test_data(100_00);

    Student::sort(data.clone());
}

pub fn generate_test_data(count: usize) -> Vec<Student> {
    // 预分配内存,避免 vector 扩容带来的性能损耗
    let mut rng = rand::rng();
    let mut students = Vec::with_capacity(count);

    for i in 0..count {
        students.push(Student::new(
            i as u64,
            format!("Student_{}", i),
            rng.random_range(0..=100), // 数学
            rng.random_range(0..=100), // 语文
            rng.random_range(0..=100), // 英语
        ));
    }
    students
}

输出belike:

2025-12-23T07:29:15.759951Z  INFO ex7: 第99989名,学号:42516, 姓名:Student_42516 总分:7, 语文:2 数学:1英语:4
2025-12-23T07:29:15.760054Z  INFO ex7: 第99989名,学号:65695, 姓名:Student_65695 总分:7, 语文:1 数学:1英语:5
2025-12-23T07:29:15.760159Z  INFO ex7: 第99992名,学号:41828, 姓名:Student_41828 总分:6, 语文:6 数学:0英语:0
2025-12-23T07:29:15.760265Z  INFO ex7: 第99992名,学号:93845, 姓名:Student_93845 总分:6, 语文:4 数学:2英语:0
2025-12-23T07:29:15.760370Z  INFO ex7: 第99992名,学号:99254, 姓名:Student_99254 总分:6, 语文:3 数学:2英语:1
2025-12-23T07:29:15.760447Z  INFO ex7: 第99995名,学号:88285, 姓名:Student_88285 总分:5, 语文:1 数学:3英语:1
2025-12-23T07:29:15.760566Z  INFO ex7: 第99996名,学号:78260, 姓名:Student_78260 总分:4, 语文:1 数学:3英语:0
2025-12-23T07:29:15.760702Z  INFO ex7: 第99996名,学号:69322, 姓名:Student_69322 总分:4, 语文:2 数学:0英语:2
2025-12-23T07:29:15.760823Z  INFO ex7: 第99998名,学号:38750, 姓名:Student_38750 总分:3, 语文:2 数学:0英语:1
2025-12-23T07:29:15.760930Z  INFO ex7: 第99999名,学号:14388, 姓名:Student_14388 总分:2, 语文:0 数学:1英语:1
2025-12-23T07:29:15.761039Z  INFO ex7: 第99999名,学号:96388, 姓名:Student_96388 总分:2, 语文:0 数学:2英语:0
test test::test_2 ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 21.45s

简单优化一下

十万条学生数据的测试结果是21秒,然而实验1里只用了不到2毫秒,着实奇怪

首先去掉了日志输出:

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.80s

提升明显,但还差着数量级

然后加大编译优化

cargo test --release -- --nocapture

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.06s

没有解决问题,首先的怀疑对象肯定是每次比较clone(心虚),那就把compare的参数改成引用吧

pub fn compare<T: PartialOrd>(&mut self, num1: &T, num2: &T) -> bool {
    self.bench.compare_time += 1;
    num1 > num2
}

结果:

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s

居然只剩6ms了,快了500倍,可怕,这还包括了生成随机数的时间

比较有意思的是--release ,如果正常打印日志速度变慢了两三倍(18 -> 49s),而去掉日志又变快了一个数量级(67 -> 6 ms)

快排奇怪的变差

接下来测试了5千万、两亿的数据长度,发现堆排序和归并只需要2分钟左右,而快排奇怪地慢到了6min

测试多次结果不变。会是分区策略的影响吗?

fn partition_hoare <T>(&mut self, nums: &mut [T], low: usize, high: usize) -> usize
where 
    T: PartialOrd,
    {
        let pivot_index = low;
        let mut l = low + 1;
        let mut h = high;

        loop{
            while l <= h && self.compare(&nums[h],&nums[pivot_index] ){
                h -= 1;
            }
            
            while l <= h && !self.compare(&nums[l],&nums[pivot_index] ){
                l += 1;
            }

            if l > h {
                break;
            }
            nums.swap(h,l);
            self.bench.move_time += 3;
            l += 1;
            h -= 1;
        }
        nums.swap(pivot_index,h);
        self.bench.move_time += 3;
        h
    }

测试结果是几乎没有变化

快排擅长排序散乱的数据,回看测试数据,是在正负10000区间内的随机数,是否是因为对于亿级的数据,这个区间太小碰撞太多?改成了正负一百万,发现快排的速度优势又回来了