数据结构实验10.1:内部排序的基本运算

文章目录

  • 一,实验目的
  • 二,实验内容
      • 1. 数据生成与初始化
      • 2. 排序算法实现
        • (1)直接插入排序
        • (2)二分插入排序
        • (3)希尔排序
        • (4)冒泡排序
        • (5)快速排序
        • (6)简单选择排序
        • (7)堆排序
        • (8)二路归并排序
      • 3. 统计与输出
  • 三,实验要求
      • (1)实验步骤与要求
      • (2)注意事项
  • 四、数据结构设计
  • 五,示例代码
    • 10-1.cpp源代码
  • 六,操作步骤
  • 七,运行结果


一,实验目的

  1. 深入理解排序原理:掌握排序的基本概念、分类及稳定性等特性,明确不同排序算法的适用场景。
  2. 熟练实现经典算法:通过编码实现直接插入、二分插入、希尔、冒泡、快速、简单选择、堆、二路归并等8种排序算法,加深对算法逻辑的理解。
  3. 提升算法应用能力:运用排序算法解决实际数据处理问题,通过统计比较次数和移动次数量化分析算法效率,增强算法优化思维。

二,实验内容

1. 数据生成与初始化

  • 使用随机数函数 rand() 生成范围在 [1, MAXNUM-1] 的随机整数序列(MAXNUM 设为100),确保序列长度可由用户指定(1~99)。
  • 示例序列(假设长度为8):[49, 38, 65, 97, 76, 13, 27, 49]

2. 排序算法实现

(1)直接插入排序
  • 思想:从第二个元素开始,将当前元素插入到已排序子序列的合适位置,通过顺序比较和移动实现。
  • 稳定性:稳定。
(2)二分插入排序
  • 优化:利用二分查找确定插入位置,减少比较次数,移动次数与直接插入相同。
  • 稳定性:稳定。
(3)希尔排序
  • 思想:按增量序列分组,对每组进行直接插入排序,逐步缩小增量至1。
  • 增量序列5, 3, 1(示例)。
  • 稳定性:不稳定。
(4)冒泡排序
  • 思想:相邻元素比较,逆序时交换,每趟将最大元素“冒泡”到末尾。
  • 优化:设置标记change,若某趟无交换则提前终止。
  • 稳定性:稳定。
(5)快速排序
  • 思想:选择基准值,通过分区操作将序列分为两部分,递归排序。
  • 分区策略:Hoare法(左右指针交替移动)。
  • 稳定性:不稳定。
(6)简单选择排序
  • 思想:每趟选择未排序部分的最小元素,与当前位置交换。
  • 稳定性:不稳定(相同元素相对顺序可能改变)。
(7)堆排序
  • 思想:构建大顶堆,每次将堆顶元素与末尾元素交换,调整堆结构。
  • 步骤:建堆 → 交换堆顶与末尾 → 调整堆。
  • 稳定性:不稳定。
(8)二路归并排序
  • 思想:递归分割序列,合并两个有序子序列。
  • 辅助空间:需要额外数组存储临时合并结果。
  • 稳定性:稳定。

3. 统计与输出

  • 比较次数(cp):记录关键字比较操作的总次数。
  • 移动次数(mv):记录元素赋值操作的总次数(如交换、插入等)。
  • 输出内容
    • 原始序列与排序后序列;
    • 各算法的比较次数和移动次数;
    • 算法效率对比表格。

三,实验要求

(1)实验步骤与要求

  1. 代码补全

    • 参照提供的参考程序,补全各算法中缺失的代码(如循环条件、指针移动逻辑等),确保算法逻辑正确。
    • 示例补全点
      • 快速排序分区函数中左右指针的移动条件(j--/i++);
      • 冒泡排序的交换标记change初始化与更新。
  2. 调试与测试

    • 测试用例
      • 随机序列(如长度10、50、99);
      • 特殊序列(正序、逆序、重复元素多的序列)。
    • 调试要点
      • 确保随机数生成正确,无越界访问;
      • 验证各算法排序结果是否正确,统计计数器(cpmv)是否准确。
  3. 数据记录与分析

    • 表格示例
排序算法比较次数(cp)移动次数(mv)耗时(ms)稳定性
直接插入排序352812稳定
快速排序22155不稳定
  • 分析方向
    • 比较次数与序列初始有序度的关系(如冒泡排序在正序时比较次数最少);
    • 移动次数与算法特性的关联(如归并排序移动次数较多但比较次数稳定);
    • 稳定性对特定场景的影响(如稳定排序适用于多关键字排序)。

(2)注意事项

  • 空间复杂度:归并排序需要额外空间(O(n)),其他算法多为原地排序(O(1),除堆排序的辅助空间)。
  • 时间复杂度对比
    • 最优/平均情况:快速排序、归并排序(O(n log n));
    • 最坏情况:直接插入、冒泡、简单选择(O(n²))。
  • 代码规范:添加必要注释,确保变量命名清晰(如cp为comparison count,mv为movement count)。

四、数据结构设计

#define MAXNUM 100
typedef int KeyType;				// 定义关键字类型为整型
typedef struct {
KeyType	key;					// 关键字项int 		other;				// 其他数据项
}ElemType;						// 元素类型
typedef struct {ElemType	r[MAXNUM+1];  // r[0]闲置或用作哨兵int 			length;			// 待排序元素个数
} SqList;							// 顺序表类型

五,示例代码

10-1.cpp源代码

#define MAXNUM 100
#define TRUE 1
#define FALSE 0
#include<stdio.h>
#include<stdlib.h>
#include<time.h>typedef int KeyType;
typedef struct {			// 定义元素的结构类型KeyType  key;	int      other;
} ElemType;typedef struct {			// 定义排序表结构类型ElemType r[MAXNUM+1];int length;
} SqList;//(1)创建随机数排序表
void CreatList(SqList &L) {int i;do {printf("  输入排序表长度(1-%d)==>", MAXNUM-1);scanf("%d", &L.length);} while(L.length<1 || L.length>MAXNUM-1);srand((unsigned)time(NULL));for(i=1; i<=L.length; i++) 			// 随机产生排序表L.r[i].key = 1 + rand()%(MAXNUM-1);
}// (2)直接插入排序
void InsertSort(SqList &L, int &cp, int &mv) {int i, j;for(i=2; i<=L.length; i++) {cp++;if (L.r[i].key < L.r[i-1].key) {L.r[0] = L.r[i]; mv++;for(j=i-1; L.r[0].key < L.r[j].key; j--) {L.r[j+1] = L.r[j]; cp++; mv++;}cp++; L.r[j+1] = L.r[0]; mv++;}//if}
}// (3)折半插入排序
void BinSort(SqList &L, int &cp, int &mv)  {int i, j, low, high, mid;for(i=2; i<=L.length; i++) {L.r[0] = L.r[i]; mv++;low = 1; high = i-1;while(low <= high) {		// 定位插入点mid = (low + high)/2;  cp++;if (L.r[0].key < L.r[mid].key) high = mid-1;else low = mid+1;}for(j=i-1; j>=high+1; j--) {	L.r[j+1] = L.r[j]; mv++;}L.r[high+1] = L.r[0]; mv++;}
}// (4)希尔排序
void ShellInsert(SqList &L, int dk, int &cp, int &mv)  {//对顺序表L作一趟希尔排序int i, j;for(i=dk+1; i<=L.length; i++) {cp++;if(L.r[i].key < L.r[i-dk].key) {		//需将L.r[i]插入有序增量子表mv++;L.r[0] = L.r[i];  			//L.r[i]暂存入L.r[0]for(j=i-dk; j>0 && L.r[0].key < L.r[j].key; j-=dk) {   L.r[j+dk] = L.r[j]; 		//寻找插入位置时记录后移cp++; mv++;}cp++; mv++; L.r[j+dk] = L.r[0];    //插入}//if}//for
} //ShellInsertSortvoid ShellSort(SqList &L, int &cp, int &mv)  {//按增量序列5,3,1进行希尔排序ShellInsert(L, 5, cp, mv);     //一趟增量为5的希尔排序ShellInsert(L, 3, cp, mv);     //二趟增量为3的希尔排序ShellInsert(L, 1, cp, mv);     //三趟增量为1的希尔排序
} //ShellInsertSort//(5)冒泡排序
void BubbleSort(SqList &L, int &cp, int &mv)  {int i, j, change;for(i = 1, change = TRUE; i < L.length && change; i++) {change = FALSE;for(j = 1;  j < L.length - i + 1;  ++j) {	cp++;if (L.r[j].key > L.r[j+1].key) {L.r[0] = L.r[j];    L.r[j] = L.r[j+1]; L.r[j+1] = L.r[0];  change = TRUE; mv += 3;}//if} //for         }//for
} // BubbleSort//(6)快速排序
int Partition(SqList &L, int low, int high, int &cp, int &mv) {int i, j;KeyType pivotkey;L.r[0] = L.r[low]; mv++; pivotkey = L.r[0].key; i = low; j = high;while (i < j) {while (i < j && L.r[j].key >= pivotkey) {j--; cp++;}if(i < j)  cp++;L.r[i] = L.r[j]; mv++;while (i < j && L.r[i].key <= pivotkey) {i++; cp++;}if(i < j)  cp++;L.r[j] = L.r[i]; mv++;}L.r[i] = L.r[0]; mv++;return i;
}//Partitionvoid QSort(SqList &L, int low, int high, int &cp, int &mv)  {//对L.r[low]~L.r[high]的元素进行快速排序int pivotloc;if (low < high) { pivotloc = Partition(L, low, high, cp, mv);    //一趟划分QSort(L, low, pivotloc-1, cp, mv);QSort(L, pivotloc+1, high, cp, mv);}//if
} //QSort//(7)简单选择排序
void SelectSort(SqList &L, int &cp, int &mv)  {//对顺序表作简单选择排序int i, j, k;				// j保存剩余元素中最小值元素的下标for(i=1; i<L.length; i++) {for(k=i, j=i; k<=L.length; k++) {cp++;if(L.r[k].key < L.r[j].key)    j = k;}if (j != i)  {L.r[0] = L.r[i]; L.r[i] = L.r[j]; L.r[j] = L.r[0]; mv += 3;} } //for          
} // SelectSort//(8)堆排序
void HeapAdjust(SqList &H, int s, int m, int &cp, int &mv) {// H.r[s .. m]中除H.r[s].key外均满足堆的定义// 调整H.r[s]的关键字,使H.r[s .. m]成为一个大顶堆int j;H.r[0] = H.r[s];  mv++;for(j=2*s; j<=m; j*=2) {      //沿key较大的孩子结点向下筛选if(j<m && H.r[j].key < H.r[j+1].key) ++j; //j为key较大的记录的下标        if(j<m)  cp++;cp++;  if(H.r[0].key >= H.r[j].key)  break; H.r[s] = H.r[j];    //较大的孩子结点值换到父结点位置mv++;s = j;}H.r[s] = H.r[0]; mv++;    //H.r[0]应插入的位置在s处
} // HeapAdjustvoid HeapSort(SqList &H, int &cp, int &mv) {  //对顺序表H进行堆排序int i;for(i=H.length/2; i>0; --i)        // 把H建成大顶堆HeapAdjust(H, i, H.length, cp, mv);for(i=H.length; i>1; --i) {H.r[0] = H.r[1]; H.r[1] = H.r[i]; H.r[i] = H.r[0]; mv += 3;//堆顶记录和当前未排子序列中最后一个记录相交换HeapAdjust(H, 1, i-1, cp, mv); //将H.r[1 .. i - 1] 重新调整为大顶堆 }
}// HeapSort //(9)二路归并排序
void Merge(SqList &L, SqList &temp, int i, int m, int n, int &cp, int &mv) 
{	// 引入辅助空间tempint b = i, j, k;for(j = m+1, k = 1; i <= m && j <= n; ++k) {if (L.r[i].key < L.r[j].key) temp.r[k] = L.r[i++];else temp.r[k] = L.r[j++];cp++; mv++;}for (; i <= m; ) {temp.r[k++] = L.r[i++]; mv++;}for (; j <= n; ) {temp.r[k++] = L.r[j++]; mv++;}for(i = b, k = 1; i <= n; )  {L.r[i++] = temp.r[k++]; mv++;}
} // Mergevoid MergeSort(SqList &L, SqList &temp, int s, int t, int &cp, int &mv)  {//归并排序int m;if (s < t) {m = (s + t)/2;MergeSort(L, temp, s, m, cp, mv);MergeSort(L, temp, m+1, t, cp, mv);Merge(L, temp, s, m, t, cp, mv);   //合并L.r[s]~L.r[m]与L.r[m+1]~L.r[t]}//if
} // MergeSort//(10)输出排序表
void OutputList(SqList L)  {int i;for(i=1; i<=L.length; i++)printf("%3d", L.r[i].key);printf("\n");
}int main() {SqList LL, L;		//LL为待排序表,L为排序表SqList temp;		//二路归并算法中所使用的临时顺序表int cp, mv;		//cp记录元素关键字比较次数,mv记录元素移动次数printf("(1)创建随机数排序表......\n");CreatList(LL);		//待排序序列保存在LL表中printf("  排序表输出:");OutputList(LL);getchar();printf("(2)直接插入排序......\n");L = LL; cp = mv = 0;InsertSort(L, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(3)折半插入排序......\n");L = LL; cp = mv = 0;BinSort(L, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(4)希尔排序......\n");L = LL; cp = mv = 0;ShellSort(L, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(5)冒泡排序......\n");L = LL; cp = mv = 0;BubbleSort(L, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(6)快速排序......\n");L = LL; cp = mv = 0;QSort(L, 1, L.length, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(7)简单选择排序......\n");L = LL; cp = mv = 0;SelectSort(L, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(8)堆排序......\n");L = LL; cp = mv = 0;HeapSort(L, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);getchar();printf("(9)二路归并排序......\n");L = LL; cp = mv = 0;MergeSort(L, temp, 1, L.length, cp, mv);printf("  排序结果:");OutputList(L);printf("  排序效率:比较次数%d,移动次数%d。\n", cp, mv);return 0;
}

六,操作步骤

1,双击Visual Studio程序快捷图标,启动程序。
在这里插入图片描述

2,之前创建过项目的话,直接打开即可,这里选择【创建新项目】。
在这里插入图片描述

3,单击选择【空项目】——单击【下一步】按钮。
在这里插入图片描述

4,编辑好项目的名称和存放路径,然后单击【创建】按钮。
在这里插入图片描述

5,创建C++程序文件,右击【源文件】——选择【添加】——【新建项】。
在这里插入图片描述
6,输入项目名称10-1.cpp,单击【添加】按钮。
在这里插入图片描述

7,编写代码,单击运行按钮,运行程序。
在这里插入图片描述

七,运行结果

1,实验要求的测试结果。
在这里插入图片描述

2,编写代码测试测试的结果。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从秒开到丝滑体验!WebAssembly助力ZKmall商城重构 B2B2C 商城性能基线

在 B2B2C 电商领域&#xff0c;用户对页面加载速度与交互流畅度的要求日益严苛。传统 Web 技术在处理复杂业务逻辑、海量数据渲染时&#xff0c;常出现卡顿、延迟等问题&#xff0c;导致用户流失。ZKmall 商城创新性地引入 WebAssembly&#xff08;简称 Wasm&#xff09;技术&a…

FD+Mysql的Insert时的字段赋值乱码问题

方法一 FDQuery4.SQL.Text : INSERT INTO 信息表 (中心, 分组) values(:中心,:分组); FDQuery4.Params[0].DataType : ftWideString; //必须加这个数据类型的定义&#xff0c;否则会有乱码 FDQuery4.Params[1].DataType : ftWideString; //ftstring就不行&#xff0c;必须是…

vue2.0 组件生命周期

个人简介 &#x1f468;‍&#x1f4bb;‍个人主页&#xff1a; 魔术师 &#x1f4d6;学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全栈发展 &#x1f6b4;个人状态&#xff1a; 研发工程师&#xff0c;现效力于政务服务网事业 &#x1f1e8;&#x1f1f3;人生格言&…

使用GmSSL v3.1.1实现SM2证书认证

1、首先使用gmssl命令生成根证书、客户端公私钥&#xff0c;然后使用根证书签发客户端证书&#xff1b; 2、然后编写代码完成认证功能&#xff0c;使用根证书验证客户端证书是否由自己签发&#xff0c;然后使用客户端证书验证客户端私钥对随机数的签名是否正确。 第一部分生成根…

升级mysql (rpm安装)

#备份以防万一 备份配置文件: /etc/my.cnf.d/server.cnf 备份数据: mysqldump -u your_username -p --all-databases > all_databases.sql #停止 systemctl stop mysql #卸载旧版 yum remove mariadb #安装新版( 通过yum安装报错,死活安装不了,只能rpm安装) 下载地址…

深入理解pip:Python包管理的核心工具与实战指南

# 深入理解pip&#xff1a;Python包管理的核心工具与实战指南 在Python开发中&#xff0c;第三方库是提升效率的关键。而pip作为Python官方的包管理工具&#xff0c;承担着安装、卸载、升级和管理库的重要职责。本文将全面解析pip的核心命令&#xff0c;结合实例演示用法&#…

Linux配置SSH密钥认证

介绍 配置SS秘钥认证后&#xff0c;可以通过shell脚本免密删除文件或执行命令。 # 生成密钥对&#xff08;如果还没有&#xff09; ssh-keygen -t rsa# 将公钥复制到服务器 ssh-copy-id "$remote_user$remote_host"

python打卡第30天

知识点回顾&#xff1a; 一&#xff0c;导入官方库的三种手段。 使用 import 直接导入整个模块 import module_name 使用 from ... import ... 导入特定功能 from module_name import function_name 使用 as 关键字重命名模块或功能 import module_name as alias # 或 from mod…

Java基础(网络编程)

一、概述 目的&#xff1a;网络通信&#xff1a; 1、设备和设备 2、进程和进程 1&#xff09;不同设备之间 2&#xff09;本地设备之间 需要解决的问题&#xff1a; 如何准确地发送到对方的主机 - IP地址 - 唯一的定位网络中的一台主机 如何准确的发送到对方主机的进程 -…

第二届parloo杯的RSA_Quartic_Quandary

&#xff08;害&#xff0c;还是太菜了&#xff0c;上去秒了一道题之后就动不了了&#xff0c;今晚做个记录&#xff0c;一点点的往回拾起吧&#xff09; # from Crypto.Util.number import getPrime, bytes_to_long # import math # # FLAG b************** # # # def gene…

RLᵛ_ Better Test-Time Scaling by Unifying LLM Reasoners With Verifiers

RLᵛ: Better Test-Time Scaling by Unifying LLM Reasoners With Verifiers 在人工智能领域&#xff0c;大语言模型&#xff08;LLM&#xff09;的推理能力提升一直是研究热点。今天要解读的论文提出了一种全新的强化学习框架RLᵛ&#xff0c;通过融合推理与验证能力&#xf…

VS中将控制台项目编程改为WINDOWS桌面程序

有时候因为误操作&#xff0c;建立了控制台项目&#xff0c;但是实际上想建立桌面程序。那么应该如何改过来呢&#xff1f; 一共要修改两个地方&#xff0c;修改步骤如下&#xff1a; 第一处修改地点&#xff1a; 将C/C下面的预处理器选项中&#xff0c;将原本的_CONSOLE修改…

API Gateway REST API 集成 S3 服务自定义 404 页面

需求分析 使用 API Gateway REST API 可以直接使用 S3 作为后端集成对外提供可以访问的 API. 而当访问的 URL 中存在无效的桶, 或者不存在的对象时, API Gateway 默认回向客户端返回 200 状态码. 而实际上这并不是正确的响应, 本文将介绍如何自定义返回 404 错误页面. 基本功…

【达梦数据库】过程、函数、包头和包体详解零基础

目录 背景参考链接解释包头包体 背景 最近遇到关于包头和包体的问题&#xff0c;学习并记录 参考链接 参考链接: oracle的过程、函数、包头和包体详解零基础 解释 包头主要用于定义接口&#xff0c;包体主要用以实现包体中声明的存储过程、函数等。 包头 包体

C++字符串处理:`std::string`和`std::string_view`的区别与使用

在 C中&#xff0c;std::string和std::string_view都用于处理字符串&#xff0c;但它们的用途和性能特点有很大不同。本教程将通过代码示例和流程图&#xff0c;帮助你快速掌握它们的使用方法。 1.什么是std::string和std::string_view&#xff1f; 1.1std::string std::str…

Pod 节点数量

动态调整 在 Kubernetes 中&#xff0c;如果为量化交易系统的 Pod 设置了可伸缩&#xff08;HPA / VPA / 自定义控制器&#xff09;&#xff0c;并且默认副本数是 5&#xff0c;那么节点数量&#xff08;副本数&#xff09;是否变化&#xff0c;主要取决于以下几个因素。 ✅ …

基于OpenCV中的图像拼接方法详解

文章目录 引言一、图像拼接的基本流程二、代码实现详解1. 准备工作2. 特征检测与描述detectAndDescribe 函数详解&#xff08;1&#xff09;函数功能&#xff08;2&#xff09;代码解析&#xff08;3&#xff09;为什么需要这个函数&#xff1f;&#xff08;4&#xff09;输出数…

Java-List集合类全面解析

Java-List集合类全面解析 前言一、List接口概述与核心特性1.1 List在集合框架中的位置1.2 List的核心特性1.3 常见实现类对比 二、ArrayList源码剖析与应用场景2.1 内部结构与初始化2.2 动态扩容机制2.3 性能特点与最佳实践 三、LinkedList 源码剖析与应用场景3.1 内部结构与节…

Flink 并行度的设置

在 Apache Flink 中&#xff0c;并行度&#xff08;Parallelism&#xff09; 是控制任务并发执行的核心参数之一。Flink 提供了 多个层级设置并行度的方式&#xff0c;优先级从高到低如下&#xff1a; &#x1f9e9; 一、Flink 并行度的四个设置层级 层级描述设置方式Operator…

OpenCV 笔记(39):频域中的拉普拉斯算子

1. 拉普拉斯算子 在该系列的第八篇文章中&#xff0c;我们曾经介绍过在二维空间拉普拉斯算子的定义为&#xff1a; 这是对函数 的二阶偏导数之和。 2. 拉普拉斯算子的傅里叶变换及其推导 在该系列的第三十二篇文章中&#xff0c;我们曾给介绍过下面的公式 二维连续傅里叶变换&…