排序 | 冒泡排序的优化与qsort快速排序
冒泡排序
冒泡排序 Bubble_Sort,是极为简单的一种排序算法。虽然效率差一点,但好在具有结构简单,容易理解,易于操作等优点。冒泡排序就是把小的元素往前调或者把大的元素往后调。在相邻的两个元素间比较,交换也发生在这两个元素之间。
冒泡排序是一种稳定排序算法,在排序后相同元素的前后顺序并没有改变。
相比于传统的冒泡排序,平均时间复杂度为O(n2),最好的时间复杂度为2,是一种效率不高的的排序。但胜在使用方便,于是便有了一些对于冒泡的优化算法。
这里,我总结了以下两种优化方案:
- 使用标记,在冒泡排序有序后提前退出排序
- 在两个方向上来回进行进行冒泡,使待排数列每次缩短两步
为了不使程序太过复杂,这里我们采用类似于波动曲线的一种方式进行方案二算法的设计。另外方案二与方案一也可以相互结合使用。三种排序算法特点如下所示:

测试三种排序效率
这里我们直接开始测试三种排序的效率,通过以下代码测试最终的优化排序方案,最后与标椎库<stdlib.h>中提供的qsort() 函数进行比较。
辅助函数与主程序代码部分
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 交换两整型变量
void Swap_Int(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
// 输出arr数组中start下标~stop下标
void Show_Ar(int* arr, int indexStart, int indexStop)
{
for (int i = indexStart; i < indexStop; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
/* 三种冒泡排序 */
void Bubble_Sort1(int *arr, int n);
void Bubble_Sort2(int* arr, int n);
void Bubble_Sort3(int* arr, int n);
// 测试各排序算法的速度
void Speed(void (*pFun)(int *, int ));
int main()
{
Speed(Bubble_Sort1);
Speed(Bubble_Sort2);
Speed(Bubble_Sort3);
return 0;
}
测试三种排序的速度方法
// 测试排序算法的速度
void Speed(void (*pFun)(int*, int))
{
// 测试对十万个数字的排序
const int n = 100000;
// 用于测试排序功能是否正常
//int arr[n] = { 67,45,90,78,89,23,34,100,12,56 };
int arr[n];
// 随机赋值
srand((unsigned)time(NULL));
for (int i = 0; i < n; ++i)
{
arr[i] = rand() % 1000;
}
// 定义计时器
clock_t start, stop;
// 由于数据过多,这里输出排序后的 10000——10020
start = clock();
(*pFun)(arr, n);
Show_Ar(arr, 10000, 10020);
stop = clock();
// 输出三种排序所用时间
printf("排序用时 %d 秒\n", (stop - start) / CLOCKS_PER_SEC);
}
三种冒泡排序方法
// 常规的冒泡函数
void Bubble_Sort1(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
Swap_Int(&arr[j], &arr[j + 1]);
}
}
}
}
// 优化——使用标记
void Bubble_Sort2(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{
// 优化:判断有序
int flg = 0;
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
Swap_Int(&arr[j], &arr[j + 1]);
flg = 1;
}
}
if (flg == 0) break;
}
}
// 优化——正反双向排序
void Bubble_Sort3(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{ // 优化:判断有序
int flg = 0;
// 正着跑
int j;
for (j = i; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
Swap_Int(&arr[j], &arr[j + 1]);
flg = 1;
}
}
if (flg == 0) break;
// 反着跑
for (int k = j - 1; k > i; --k)
{
if (arr[k] < arr[k - 1])
{
Swap_Int(&arr[k], &arr[k - 1]);
flg = 1;
}
}
if (flg == 0) break;
}
}
测试三个冒泡排序对10万个数据进行排序后的运行结果:
第一次测试截图:

第二次测试截图:

第三次测试截图:

qsort 排序算法测试
qsort() 是C标椎库中自带的一种快速排序算法,排序速度相当快,并且是一种泛型的排序算法。下面是 qsort() 的用法。
// <stdlib.h>
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
base -- 指向要排序的数组的第一个元素的指针。
nitems -- 由 base 指向的数组中元素的个数。
size -- 数组中每个元素的大小,以字节为单位。
compar -- 用来比较两个元素的函数。
上图为三种排序的执行结果,接下来使用 qsort() 函数进行排序测试。这里也使用Speed() 函数对测试排序的算法进行封装。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int Cmp_Int(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
// 输出arr数组中start下标~stop下标
void Show_Ar(int* arr, int indexStart, int indexStop)
{
for (int i = indexStart; i < indexStop; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void Speed();
int main()
{
int n = 3;
while (n--)
{
Speed();
}
return 0;
}
// 测试排序算法的速度
void Speed()
{
// 测试对十万个数字的排序
const int n = 100000;
int arr[n];
// 随机赋值
srand((unsigned)time(NULL));
for (int i = 0; i < n; ++i)
{
arr[i] = rand() % 1000;
}
// 定义计时器
clock_t start, stop;
// 由于数据过多,这里输出排序后的 10000——10020
start = clock();
qsort(arr, n, sizeof(int), Cmp_Int);
Show_Ar(arr, 10000, 10020);
stop = clock();
// 输出排序所用时间
printf("排序用时 %d 秒\n", (stop - start) / CLOCKS_PER_SEC);
}
第一次排序结果:(由于排序速度过快,不到1秒中就完成了十万个数据的排序)

由于我们计算机栈区的大小限制,不便于测试更大的数据。所以我们改从堆区申请一百万数据进行排序。要知道栈由系统自动分配,速度较快,堆区由程序猿手动申请,且操作效率要慢于栈区。
修改源码,其中///***////中内容为修改处
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
int Cmp_Int(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
////////////////////////////////////////////////////////////////////////
// 输出arr数组中start下标~stop下标,以step步长 //增加步长参数
void Show_Ar_Step(int* arr, int indexStart, int indexStop, int step)
{
for (int i = indexStart; i < indexStop; i+=step)
{
printf("%d ", arr[i]);
}
printf("\n");
}
///////////////////////////////////////////////////////////////////////
void Speed();
int main()
{
int n = 3;
while (n--)
{
Speed();
}
return 0;
}
// 测试排序算法的速度
void Speed()
{
// 测试对一千万个数字的排序
const int n = 10000000; //////////////
//////////////////////////////////////////////////////////////////
int* arr = (int*)malloc(sizeof(int) * n); //堆区申请
assert(arr != NULL);
/////////////////////////////////////////////////////////////////
// 打印arr数组的容量
printf("共有数据 %d 个\n", n ); ////////////
// 随机赋值
srand((unsigned)time(NULL));
for (int i = 0; i < n; ++i)
{
arr[i] = rand() % 1000;
}
// 定义计时器
clock_t start, stop;
// 由于数据过多,这里输出排序后的 100000——500000,步长为10000
start = clock();
qsort(arr, n, sizeof(int), Cmp_Int);
Show_Ar_Step(arr, 100000, 300000,10000); //////////
stop = clock();
// 输出排序所用时间
printf("排序用时 %d 秒\n", (stop - start) / CLOCKS_PER_SEC);
}
对一千万数据进行排序后的输出结果:

可以看到,qsort() 排序真的是非常的快,无论对冒泡排序怎样的优化之间的差距还是非常的大。
以下,是本次测试的数据对比:
冒泡排序: 十万数据 平均用时73秒 (完败)
优化冒泡: 十万数据 平均用时64秒 (有效果)
qsort排序: 千万数据 平均用时 3秒 (完胜)
虽然,C标椎库中为我们实现了排序算法,我们也不能太过依赖标椎库。每一种算法都有他独特的思想在其中,在平时的练习中也要多加练习其他的排序算法。另外,qsort是基于快排优化而来,下次我们可以通过优化快排和qsort进行测试比较。
附:C语言泛型编程——泛型冒泡排序: https://blog.csdn.net/weixin_43919932/article/details/90573445
排序 | 冒泡排序的优化与qsort快速排序的更多相关文章
- Linux C++ 直接选择排序,冒泡排序,快速排序
选择排序的思想是:每次从待排序中选择最小(大)的元素插入已经排好的序列中. /*直接选择排序*/ #include <iostream> using namespace std; void ...
- Java 排序算法-冒泡排序及其优化
Java 排序算法-冒泡排序及其优化 什么是冒泡排序 基本写法 优化后写法 终极版本 源码及测试 什么是冒泡排序 这里引用一下百度百科上的定义: 冒泡排序(Bubble Sort),是一种计算机科学领 ...
- java实现几种常用排序:冒泡排序
一.冒泡排序介绍 冒泡排序是我们得最多的排序方式之一,原因是简单易实现,且原理易懂.顾名思义,冒泡排序,它的排序过程就像水中的气泡一样,一个一个上浮到水面. 二.冒泡排序原理分析 三.冒泡排序代码实现 ...
- Java数据结构之排序---冒泡排序
冒泡排序的基本思想: 通过对待排序序列从前到后(从下标小的元素开始),依次比较相邻位置的元素的值,若发现与给定的次序冲突,则交换位置(假设数值大的数放在序列的后面),使数值较大的元素逐渐从前移动到后部 ...
- java排序算法(五):快速排序
java排序算法(五):快速排序 快速排序是一个速度非常快的交换排序算法,它的基本思路很简单,从待排的数据序列中任取一个数据(如第一个数据)作为分界值,所有比它小的元素放到左边.所有比它大的元素放到右 ...
- 排序算法入门之冒泡排序及其优化(java实现)
冒泡排序思想(从小到大): 比较相邻两个元素,如果第一个元素比第二个元素大,就交换他们的位置.第一趟,从第一个元素开始,第一个元素和第二个元素比较,如果第一个元素比第二个元素大,则交换位置:接下来比较 ...
- 【Java】 大话数据结构(14) 排序算法(1) (冒泡排序及其优化)
本文根据<大话数据结构>一书,实现了Java版的冒泡排序. 更多:数据结构与算法合集 基本概念 基本思想:将相邻的元素两两比较,根据大小关系交换位置,直到完成排序. 对n个数组成的无序数列 ...
- 【程序员笔试面试必会——排序①】Python实现 冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序、希尔排序
最近在准备笔试题和面试题,把学到的东西整理出来,一来是给自己留个笔记,二来是帮助大家学习. 题目: 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: 输入:[1,2,3,5,2, ...
- java 合并排序算法、冒泡排序算法、选择排序算法、插入排序算法、快速排序算法的描述
算法是在有限步骤内求解某一问题所使用的一组定义明确的规则.通俗点说,就是计算机解题的过程.在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法.前者是推理实现的算法,后者是操作实现的算法. ...
随机推荐
- 查询性能提升3倍!Apache Hudi 查询优化了解下?
从 Hudi 0.10.0版本开始,我们很高兴推出在数据库领域中称为 Z-Order 和 Hilbert 空间填充曲线的高级数据布局优化技术的支持. 1. 背景 Amazon EMR 团队最近发表了一 ...
- C#中?和:?和??代表什么
?代表可空类型修饰符 引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空.为了使值类型也可为空,就可以使用可空类型?:带便三元表达式 int a=b>c?b:c 如果 ...
- 初识python(2)
目录 引言 数据类型 字典 集合 元组 布尔值 用户交互 格式化输出 运算符 增量赋值 链式赋值 交叉赋值 解压赋值 逻辑运算符 成员运算符 身份运算符 引言 小伙伴们昨天已经讲了一点python的数 ...
- 用Assert(断言)封装异常,让代码更优雅(附项目源码)
有关Assert断言大家并不陌生,我们在做单元测试的时候,看业务事务复合预期,我们可以通过断言来校验,断言常用的方法如下: public class Assert { /** * 结果 = 预期 则正 ...
- CSS复合选择器,元素的显示模式,CSS背景设置
欢迎大家去博客冰山一树Sankey,浏览效果更好.直接右上角搜索该标题即可 博客园主页:博客园主页-冰山一树Sankey CSDN主页:CSDN主页-冰山一树Sankey 前端学习:学习地址:黑马程序 ...
- json web token JWT实现TP5创建和验证
根据博客进行jwt初始化配置 https://blog.csdn.net/weixin_43389208/article/details/117442266?spm=1001.2014.3001.55 ...
- PHP-文件上传封装类
<?php $file = $_FILES['img']; $obj = new File(); $res = $obj->upload($file,'upload'); if($res) ...
- 1. 企业级调度器LVS初识、工作模式详解
想学习更多相关知识请看博主的个人博客 1. LVS官网 LVS 时全球最流程的四层负载均衡开源软件. LVS 官网:http://www.linuxvirtualserver.org/ 2.Linux ...
- 七天接手react项目 系列 —— react 脚手架创建项目
其他章节请看: 七天接手react项目 系列 react 脚手架创建项目 前面我们一直通过 script 的方式学习 react 基础知识,而真实项目通常是基于脚手架进行开发. 本篇首先通过 reac ...
- 【基础】工作中常用的linux命令,经常会被面试官问到
前言 面试经常会问到一些Linux操作命令,下面就工作中常用的和面试问的频率较高的命令做详细描述. 常用命令 修改密码:passwd 用户名 切换用户名:su 用户名 查看当前路径:pwd 调整路径: ...