【数据结构与算法】内排序算法比较(C\C++)

实践要求

1. 问题描述

各种内部排序算法的时间复杂度分析结果只给出了算法执行时间的阶,或大概执行时间,试通过随机的数据比较各算法的关键字比较次数和关键字移动次数,以取得直观感受。


2. 基本要求

  1. 对以下10种常用的内部排序算法进行比较:直接插入排序、折半插入排序、二路插入排序、希尔排序、起泡排序、快速排序、简单选择排序、堆排序、归并排序、基数排序。
  2. 待排序表的表长不少于100;其中的数据要用伪随机数产生程序产生;至少要用5组不同的输入数据作比较;比较的指标为有关键字参加的比较次数和关键字的移动次数(关键字交换计为3次移动)。

3. 测试数据

由随机数产生器决定。


4. 实现提示

主要工作是设法在程序中的适当地方插入计数操作。程序还可以包括计算几组数据均值的操作。最后要对结果作出简单分析,包括对各组数据得出结果波动大小的解释。注意分块调试的方法。


实践报告

1. 题目分析

说明程序设计的任务,强调的是程序要做什么,此外列出各成员分工

程序设计任务:
比较十种不同的内排序算法


2. 程序设计

实现概要设计中的数据类型,对主程序、模块及主要操作写出伪代码,画出函数的调用关系

十种内排序算法


3. 测试结果

列出测试结果,包括输入和输出

在这里插入图片描述


4. 用户使用说明

给出主界面及主要功能界面

无需任何操作,自动测试


5. 附录

源程序文件清单:
sorce.cpp

6. 全部代码

source.cpp

#include <algorithm>
#include <iostream>
#include <random>
#include <vector>
using namespace std;

// 关键字参加的交换次数
long long comparison_times = 0;

// 关键字参加的移动次数
long long move_times = 0;

// 随机数生成器,生成int范围内的整数
std::random_device seed;
std::default_random_engine engine{seed()};
std::uniform_int_distribution<int> dis(INT_MIN, INT_MAX);

// 排序算法 - 冒泡排序,
// 通过相邻元素的比较和交换,在每一轮使一个元素移动到正确的位置。
// 时间复杂度O(n^2)
// 总体来说,选择排序每次交换都是使得最小值移至最前,效率略高一点。冒泡排序每次比较都可能发生交换,效率略低。
void bubbling_sort(vector<int> &arr) {
    for (int i = 0; i < arr.size(); ++i) {
        for (int j = 1; j < arr.size() - i; ++j) {
            ++comparison_times;
            if (arr[j] < arr[j - 1]) {
                swap(arr[j], arr[j - 1]);
                move_times += 3;
            }
        }
    }
}

// 排序算法 - 简单选择排序
// 每一轮从未排序的元素中选出最小的元素,使其移动到已排序的序列的末尾。
// 时间复杂度O(n^2)
void simple_selection_sort(vector<int> &arr) {
    for (int i = 0; i < arr.size(); ++i) {
        ++move_times;
        int min_index = i;
        for (int j = i; j < arr.size(); ++j) {
            ++comparison_times;
            if (arr[min_index] > arr[j]) {
                min_index = j;
                ++move_times;
            }
        }
        swap(arr[min_index], arr[i]);
        move_times += 3;
    }
}

// 排序算法 - 快速排序算法
void quick_sort(vector<int> &arr, int left, int right) {
    // 递归终止条件:区间只有0或1个元素,已然有序,无需继续划分
    if (right - left < 1) {
        return;
    }

    // 随机选取一个数作为基准数Pivot
    int index = left + rand() % (right - left + 1);
    int pivot = arr[index];
    ++move_times;

    // 初始化左右指针lt和gt,cnt用于遍历,lt表示小于pivot的最后一个元素,gt表示大于pivot的第一个元素
    int lt = left;
    int gt = right;
    int cnt = left;

    // 遍历数组,进行三向切分
    while (cnt <= gt) {
        // 当前元素小于pivot,则交换至左指针lt处,lt和cnt同时右移
        ++comparison_times;
        if (arr[cnt] < pivot) {
            swap(arr[cnt++], arr[lt++]);
            move_times += 3;
        }
        // 当前元素大于pivot,则交换至右指针gt处,gt左移
        else if (arr[cnt] > pivot) {
            swap(arr[gt--], arr[cnt]);
            move_times += 3;
        }
        // 等于pivot,直接跳过
        else {
            ++cnt;
        }
    }

    // 递归调用,继续对左右两部分进行快速排序
    quick_sort(arr, left, lt - 1);
    quick_sort(arr, gt + 1, right);
}

// 排序算法 - 归并排序:通过递归将数组划分为两部分,排序后合并得到最终结果。
void merge_sort(vector<int> &tmp, vector<int> &arr, int left, int right) {
    if (right - left < 1) {  // 递归终止条件,子数组长度为 1
        return;
    }
    int mid = left + ((right - left) >> 1);  // 取中间索引
    merge_sort(tmp, arr, left, mid);         // 对左半部分排序
    merge_sort(tmp, arr, mid + 1, right);    // 对右半部分排序

    int l = left, r = mid + 1, k = 0;  // 初始化变量
    while (l <= left && r <= right) {  // 双指针,取较小者
        ++comparison_times;
        ++move_times;
        if (arr[l] < arr[r]) {
            tmp[k++] = arr[l++];  // 将较小值放入 tmp,指针后移
        } else {
            tmp[k++] = arr[r++];
        }
    }
    while (l <= mid) {  // 将左半部分剩余元素放入 tmp
        ++move_times;
        tmp[k++] = arr[l++];
    }
    while (r <= right) {  // 将右半部分剩余元素放入 tmp
        ++move_times;
        tmp[k++] = arr[r++];
    }
    copy(tmp.begin(), tmp.begin() + (right - left + 1),
         arr.begin() + left);  // 将 tmp 拷贝回 arr
    move_times += right - left + 1;
}

// 排序算法 - 简单插入排序
void simple_insertion_sort(vector<int> &arr) {
    for (int i = 1; i < arr.size(); ++i) {
        for (int j = i - 1; j >= 0; --j) {
            ++comparison_times;
            if (arr[j + 1] < arr[j]) {
                move_times += 3;
                swap(arr[j + 1], arr[j]);
            } else {
                break;
            }
        }
    }
}

// 排序算法 - 折半插入排序
void half_insert_sort(vector<int> &arr) {
    for (int i = 1; i < arr.size(); ++i) {
        int left = 0;
        int right = i - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            ++comparison_times;
            if (arr[mid] > arr[i]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        for (int k = i; k >= left; --k) {
            swap(arr[left], arr[k]);
            move_times += 3;
        }
    }
}

// 排序算法 - 二路插入排序
// 将数组分成已排序和未排序两部分,每次从未排序的部分找一个最小的元素插入到已排序的部分中。
// 时间复杂度O(n^2)
void two_way_insertion_sort(vector<int> &arr) {
    for (int i = 1; i < arr.size(); ++i) {
        ++move_times;
        int key = arr[i];
        int j = i - 1;
        // 找到已排序部分第一个大于等于key的元素,并记录其索引
        while (j >= 0 && arr[j] > key && (++comparison_times)) {
            ++move_times;
            arr[j + 1] = arr[j];
            j--;
        }
        // 在已排序部分的正确位置插入key
        arr[j + 1] = key;
        ++move_times;
    }
}

// 排序算法 - 基数排序
// 针对每一位数进行排序,从低位到高位逐渐排序,实现整体有序。要求元素的表示形式从低位到高位是有意义的。
// 时间复杂度O(n*k),其中k是排序位数。
void radix_sort(vector<int> &arr) {
    // 获取最大数,确定排序位数
    int max_num = *max_element(arr.begin(), arr.end());
    comparison_times += arr.size();

    int num_digits = 0;
    while (max_num > 0) {
        max_num /= 10;
        num_digits++;
    }

    // 设置10个桶
    vector<vector<int>> buckets(10);
    // 按位排序,从个位开始
    for (int pos = 0; pos < num_digits; pos++) {
        // 将所有整数按指定位数放入桶中
        for (int num : arr) {
            int digit = 0;
            buckets[digit].push_back(num);
            ++move_times;
        }

        // 按桶顺序输出
        move_times += arr.size();
        arr.clear();

        for (auto &bucket : buckets) {
            for (int num : bucket) {
                ++move_times;
                arr.push_back(num);
            }
            bucket.clear();
        }
    }
}

// 排序算法 - 希尔排序
// 是插入排序的一种优化版本。它通过间隔为h的增量来比较并交换相隔h个元素,采用递减的h值,最终当h=1时,变成普通的插入排序。
// 时间复杂度O(nlogn)
void shell_sort(vector<int> &arr) {
    int h = 1;
    while (h < arr.size() / 3) {
        h = 3 * h + 1;  // 确定初始步长h
    }

    while (h >= 1) {
        for (int i = h; i < arr.size(); i++) {
            int j = i;
            int temp = arr[i];
            ++move_times;
            while (j >= h && arr[j - h] > temp && ++comparison_times) {
                ++move_times;
                arr[j] = arr[j - h];
                j -= h;
            }
            ++move_times;
            arr[j] = temp;
        }
        h /= 3;  // 步长缩小
    }
}

// 排序算法 - 堆排序,利用堆结构(可看成完全二叉树)的特点实现排序。
// 时间复杂度O(nlogn)
void heapify(vector<int> &arr, int n, int i) {
    int largest = i;    // 目前最大值的索引
    int l = 2 * i + 1;  // 左子节点索引
    int r = 2 * i + 2;  // 右子节点索引

    comparison_times += 2;
    if (l < n && arr[l] > arr[largest]) largest = l;
    if (r < n && arr[r] > arr[largest]) largest = r;
    if (largest != i) {
        swap(arr[i], arr[largest]);
        move_times += 3;
        heapify(arr, n, largest);
    }
}

void heap_sort(vector<int> &arr) {
    // 建立最大堆,将数组转换成最大堆
    for (int i = arr.size() / 2 - 1; i >= 0; i--) heapify(arr, arr.size(), i);

    // 交换根节点和最后一个节点,调整最大堆,重复此操作
    for (int i = arr.size() - 1; i >= 0; i--) {
        move_times += 3;
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

void restore_status(vector<int> &tmp, vector<int> &arr,
                    long long &comparison_times, long long &move_times) {
    tmp = arr;
    comparison_times = 0;
    move_times = 0;
    return;
}

void print_statistical_results(const long long &comparison_times,
                               const long long &move_times) {
    cout << comparison_times << ' ' << move_times << '\n';
}

int main() {
    // ios::sync_with_stdio(false);
    int step = 10;
    for (int i = 0; i < 5; ++i, step *= 5) {
        vector<int> arr, tmp;
        for (int j = 0; j < step; ++j) {
            arr.push_back(dis(engine));
        }

        // 冒泡排序
        restore_status(tmp, arr, comparison_times, move_times);
        bubbling_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 简单选择排序
        restore_status(tmp, arr, comparison_times, move_times);
        simple_selection_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 快速排序
        restore_status(tmp, arr, comparison_times, move_times);
        quick_sort(tmp, 0, tmp.size() - 1);
        print_statistical_results(comparison_times, move_times);

        // 归并排序
        restore_status(tmp, arr, comparison_times, move_times);
        vector<int> temporary(arr.size());
        merge_sort(temporary, tmp, 0, arr.size() - 1);
        print_statistical_results(comparison_times, move_times);

        // 简单插入排序
        restore_status(tmp, arr, comparison_times, move_times);
        simple_insertion_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 折半插入排序
        restore_status(tmp, arr, comparison_times, move_times);
        half_insert_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 二路插入排序
        restore_status(tmp, arr, comparison_times, move_times);
        two_way_insertion_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 基数排序
        restore_status(tmp, arr, comparison_times, move_times);
        radix_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 希尔排序
        restore_status(tmp, arr, comparison_times, move_times);
        shell_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        // 堆排序
        restore_status(tmp, arr, comparison_times, move_times);
        heap_sort(tmp);
        print_statistical_results(comparison_times, move_times);

        cout << endl;
    }
    cout << "end";
    return 0;
}


结束语

  因为是算法小菜,所以提供的方法和思路可能不是很好,请多多包涵~如果有疑问欢迎大家留言讨论,你如果觉得这篇文章对你有帮助可以给我一个免费的赞吗?我们之间的交流是我最大的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/35263.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【mysql实践】如何查看阿里云RDS的MySQL库中的binlog日志

背景&#xff1a; 工作中我们为了查看MySQL中数据修改的历史记录时&#xff0c;会通过查看binlog日志。但由于binlog日志是二进制文件&#xff0c;需要解析之后&#xff0c;才能用文本查看工具打开。这次笔者使用flink进行实时统计时就多次遇到了这个问题。经常看笔者最近博客…

redhat6安装mysql8.0.33

1、下载mysql 官网地址&#xff1a;https://downloads.mysql.com/archives/community/ 下载步骤&#xff1a; 过滤操作系统版本 下载后&#xff0c;上传到服务器Downloads目录 2、安装mysql8 解压压缩包 tar -xvf mysql-8.0.31-1.el9.x86_64.rpm-bundle.tar [rootrhel64 …

山海鲸Cesium:帮你用更简单的方式升级视效

CesiumJS作为绝大多数人都在用的开源地球可视化引擎&#xff0c;视觉效果并不拔尖&#xff0c;这让很多giser都想着有一天升级一下视效&#xff0c;从众多平庸的项目中脱颖而出。然而&#xff0c;对于一些使用Cesium的项目来说&#xff0c;要想达到Cesium for unreal的视觉效果…

Jetson Orin Nano Developer Kit

Jetson Orin Nano Developer Kit包括Jetson Orin Nano 8GB模块&#xff0c;该模块具有NVIDIA安培GPU(具有1024个CUDA内核和32个第三代张量内核)和6核ARM CPU&#xff0c;能够运行多个并发AI应用程序管道并提供高推断性能。 开发套件载体板支持所有Jetson Orin Nano和Orin NX模块…

多层感知机与深度学习算法概述

多层感知机与深度学习算法概述 读研之前那会儿我们曾纠结于机器学习、深度学习、神经网络这些概念的异同。现在看来深度学习这一算法竟然容易让人和他的爸爸机器学习搞混…可见深度学习技术的影响力之大。深度学习&#xff0c;作为机器学习家族中目前最有价值的一种算法&#…

Java安全——安全提供者

Java安全 安全提供者 在Java中&#xff0c;安全提供者&#xff08;Security Provider&#xff09;是一种实现了特定安全服务的软件模块。它提供了一系列的加密、解密、签名、验证和随机数生成等安全功能。安全提供者基础设施在Java中的作用是为开发人员提供一种扩展和替换标准…

Java性能权威指南-总结26

Java性能权威指南-总结26 数据库性能的最佳实践异常日志 数据库性能的最佳实践 异常 Java的异常处理一直有代价高昂的坏名声。其代价确实比处理正常的控制流高一些&#xff0c;不过在大多数情况下&#xff0c;这种代价并不值得浪费精力去绕过。另一方面&#xff0c;因为异常处…

【面试】美团面试真题和答案

文章目录 前言1.线程池有几种实现方式&#xff1f;2.线程池的参数含义&#xff1f;3.锁升级的过程&#xff1f;4.i 如何保证线程安全&#xff1f;5.HashMap和ConcurrentHashMap有什么区别&#xff1f;6.Autowired和Resource区别&#xff1f;7.说说常用的设计模式8.Redis为什么这…

SpringBoot2+Vue2实战(十二)springboot一对一,一对多查询

新建数据库表 Course Data TableName("t_course") public class Course implements Serializable {private static final long serialVersionUID 1L;/*** id*/TableId(value "id", type IdType.AUTO)private Integer id;/*** 课程名称*/private String…

微信小程序制作 购物商城首页 【内包含源码】

1、实现效果 手机效果预览,这里的首页使用到了轮播图。页面图片数据可以替换成自己的数据。 2、开发者工具效果图 3、项目的目录结构 4、首页核心代码 4.1 index.js 这里用来存放数据,页面的数据。目前是假数据,也可以调用接口接收真实数据 // index.jsimport {request }…

【我的创作纪念日】关于某站的音频爬虫+GUI

文章目录 一、前言&机遇二、爬虫代码三、爬虫GUI四、文件打包五、结果展示未来可期 一、前言&机遇 许久没看私信内容&#xff0c;一上线就看到了官方的私信&#xff0c;我已经来到CSDN1024天啦&#xff01; 想到注册这个号的初衷是学习记录爬虫&#xff0c;后面渐渐变…

【计算机视觉 | 目标检测】arxiv 计算机视觉关于目标检测的学术速递(7 月 3 日论文合集)

文章目录 一、检测相关(9篇)1.1 Federated Ensemble YOLOv5 - A Better Generalized Object Detection Algorithm1.2 Zero-shot Nuclei Detection via Visual-Language Pre-trained Models1.3 Federated Object Detection for Quality Inspection in Shared Production1.4 Comp…

【数据科学和可视化】反思十年数据科学和可视化工具的未来

数据科学在过去十年中呈爆炸式增长&#xff0c;改变了我们开展业务的方式&#xff0c;并让下一代年轻人为未来的工作做好准备。但是这种快速增长伴随着对数据科学工作的不断发展的理解&#xff0c;这导致我们在如何使用数据科学从我们的大量数据中获得可操作的见解方面存在很多…

Django的数据库配置、生成(创建)过程、写入数据、查看数据的学习过程记录

目录 01-配置数据库信息02-安装Python的MySQL数据库驱动程序 mysqlclient03-安装Mysql&#xff0c;并启动Mysql04-定义Django的数据库模型(定义数据表-编写models.py文件)05-按照数据的配置生成数据库(执行迁移命令)05-01-生成迁移执行文件05-02-执行数据库模型迁移 06-查看数据…

git bash 命令行反应慢、卡顿

1. 在Windows11的电脑上安装了git 后&#xff0c;鼠标右键打开git bash here&#xff0c;打开窗口缓慢&#xff0c;输入命令也慢的要死&#xff0c;如果安装git的时候选择在桌面创建图标&#xff0c;通过桌面图标打开也是一样的 2. 最简单的ls 命令&#xff0c;都要停顿半秒 3.…

m4a音频格式转换器:让音频轻松换装

大家有没有遇到这样的情况——你下载了一个很酷的音频文件&#xff0c;但是播放设备却说“不认识”这个格式&#xff1f;别担心&#xff01;现在有个超级厉害的工具可以帮你解决这个问题&#xff0c;它就是m4a音频格式转换器&#xff01;它能让你的音频文件变身&#xff0c;适应…

TiDB(2):TiDB架构特性

1 TiDB 整体架构 TiDB 集群主要包括三个核心组件&#xff1a;TiDB Server&#xff0c;PD Server 和 TiKV Server。此外&#xff0c;还有用于解决用户复杂 OLAP 需求的 TiSpark 组件和简化云上部署管理的 TiDB Operator 组件。 架构图解 1.1 TiDB Server TiDB Server 负责接收…

技术服务企业缺成本票,所得税高怎么解决?可有良策?

技术服务企业缺成本票&#xff0c;所得税高怎么解决&#xff1f;可有良策&#xff1f; 《税筹顾问》专注于园区招商、企业税务筹划&#xff0c;合理合规助力企业节税&#xff01; 技术服务型企业最核心的价值就是为客户提供技术支撑&#xff0c;而这类型的企业在税务方面面临的…

CSRF漏洞复现

目录 CSRF产生的条件CSRF漏洞分类CSRF漏洞危害CSRF漏洞检测CSRF漏洞修复方案利用靶场CSRF-Minefield-V1.0漏洞复现 CSRF产生的条件 一、被攻击者在登陆了web网页&#xff0c;并且在本地生成了cookie 二、在cookie未过期的情况下&#xff0c;利用同一个浏览器访问了攻击者的页…

最新版Flink CDC MySQL同步Elasticsearch(一)

1.环境准备 首先我们要基于Flink CDC MySQL同步MySQL的环境基础上&#xff08;flink-1.17.1、Java8、MySQL8&#xff09;搭建Elasticsearch7-17-10和Kibana 7.17.10。笔者已经搭建好环境&#xff0c;这里不做具体演示了&#xff0c;如果需要Es的搭建教程情况笔者其他博客 注意…
最新文章