文章目录

  • 一、排序算法概览
  • 二、算法实现
    • 1、选择排序
    • 2、冒泡排序
    • 3、插入排序
    • 4、快速排序
    • 5、希尔排序
    • 6、桶排序(基数排序)
    • 7、归并排序
    • 8、堆排序
  • 三、总结

一、排序算法概览


可以在VisuAlgo或者站长辅助工具中查看这些排序算法的动态演示过程

二、算法实现

1、选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

void selectSort(ElemType arr[], int len) //选择排序
{int temp;for (int i = 0; i < len - 1; ++i){for (int j = i + 1; j < len; ++j){if (arr[i]>arr[j]){temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}}
}

2、冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。

冒泡排序改进1:在某次遍历中如果没有数据交换,说明整个数组已经有序。因此通过设置标志位来记录此次遍历有无数据交换就可以判断是否要继续循环。

冒泡排序改进2:记录某次遍历时最后发生数据交换的位置,这个位置之后的数据显然已经有序了。因此通过记录最后发生数据交换的位置就可以确定下次循环的范围了。

void bulletSort(ElemType arr[], int len)  //冒泡排序
{int temp;for (int i = 0; i < len - 1; ++i){for (int j = 0; j < len - 1 - i; ++j){if (arr[j]>arr[j + 1]){temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}

3、插入排序

插入排序基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

void insertSort(ElemType arr[], int len)  //插入排序
{int temp,j,i;//形式1for (i = 1; i < len; ++i){temp = arr[i];j = i - 1;while (j >= 0 && temp < arr[j])//j指向的是有序序列{arr[j + 1] = arr[j];--j;}      arr[j + 1] = temp;}//形式2//for (i = 1; i < len; ++i)//{//    temp = arr[i];//   for (j = i - 1; j >= 0 && temp < arr[j]; --j)//j指向的是有序序列//  {//     arr[j + 1] = arr[j];//    }// arr[j + 1] = temp;//}//形式3/*for (i = 1; i < len; ++i){temp = arr[i];for (j = i - 1; j >= 0; --j){if (temp < arr[j]){arr[j + 1] = arr[j];}elsebreak;}arr[j + 1] = temp;}*///形式4/*for (int i = 1; i < len; ++i){for (int j = i - 1; j >= 0; --j){if (arr[j + 1] < arr[j]){temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp;}elsebreak;}}*/
}

4、快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

void quickSort(ElemType arr[], int left, int right)//快速排序
{int i = left, j = right;int temp;if (left >= right) return;while (i <= j){while (i <= j&&arr[left] >= arr[i]) ++i;//找出左边比arr[left]大的元素while (i <= j&&arr[left] <= arr[j]) --j;//找出右边比arr[left]小的元素if (i < j)//交换找到的元素{temp = arr[i];arr[i] = arr[j];arr[j] = temp;i++;//交换完之后移向下一个位置--j;}}//经过循环后在j位置就是标杆的位置,这个位置左边都不大于该值,该位置右边都不小于该值temp = arr[left];arr[left] = arr[j];arr[j] = temp;       //交换quickSort(arr, left, j - 1);  //递归操作左边元素quickSort(arr, j+1, right);   //递归操作右边元素}

5、希尔排序

希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

void shellSort(ElemType arr[], int len)  //希尔排序
{int d = len / 2;   //分组(增量置初值)int i, j;int temp;while (d > 0)      //由希尔排序要进行的次数来决定(每一次都有相应的分组情况,即有不同的增值){for (i = d; i < len; ++i)  // 对该次希尔排序(分组情况)进行插入排序(由要进行插入排序的元素个数来决定)//本来一个插入排序从1开始,这里有d组插入排序,所以从d开始  //这个是d组一起进行插入排序,即等到每一组都插入第i个元素后,才会进行插入第i+1个元素{temp = arr[i];for (j = i - d; j >= 0 && temp < arr[j]; j = j - d)  //将每次的元素插入(由有序元素的个数来决定)arr[j + d] = arr[j];arr[j + d] = temp;}d = d / 2;  //从新分组}
}

6、桶排序(基数排序)

基数排序,第一步根据数字的个位分配到每个桶里,在桶内部排序,然后将数字再输出(串起来);然后根据十位分桶,继续排序,再串起来。直至所有位被比较完,所有数字已经有序。

void radixSort(ElemType arr[], int len)//桶排序(基数排序)
{int temp[10][20];int index;for (int n = 1; n <= 100; n *= 10)//数据中最大数是几位,就要进行几次循环{for (int x = 0; x < 10; ++x)  //初始化     为了方便查找到存放的数据{for (int y = 0; y < 20; ++y){temp[x][y] = -1;}}for (int i = 0; i < len; ++i)  //存放数据{index = (arr[i] / n) % 10;temp[index][i] = arr[i];}int count = 0;for (int i = 0; i < 10; ++i)          //将该次排序后的结果放回原数组{for (int j = 0; j < 20; ++j){if (temp[i][j] != -1)arr[count++] = temp[i][j];}}}
}

7、归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

void mergeInArr(ElemType arr[], int left, int mid, int right)
{//注意此时左右空间都是各自有序int length = right - left + 1; //辅助数组长度int *p = new int[length];      //构建辅助数组memset(p, 0, sizeof(int)*length);  //给辅助数组赋值int low = left;                    //记录左区间的起始位置int hig = mid + 1;                 //记录右区间的起始位置     int index = 0;                     //辅助空间下标while (low <= mid&&hig <= right) //左右区间都没有比较完,都有数据{while (low <= mid&&arr[low] <= arr[hig])//如果左边区间没有越界,并且左区间的数值<=右区间的数值p[index++] = arr[low++];            //将小的数据存入辅助空间while (hig <= right&&arr[low] > arr[hig]) //如果右边区间没有越界,并且左区间的数值>右区间的数值p[index++] = arr[hig++];   //将小的数据存入辅助空间}//到这一步,证明起码有一个区间是合并进了辅助数组if (hig <= right)//证明右区间并没有完成合并,左区间是完成合并,把右区间剩下的数据直接拷贝到辅助数组即可(此时右区间剩下的数据比辅助空间的数据大)memcpy(&p[index], &arr[hig], sizeof(int)*(right - hig + 1));if (low <= mid)memcpy(&p[index], &arr[low], sizeof(int)*(mid - low + 1));//将排完序的值传回给原数组memcpy(&arr[left], p, sizeof(int)*length); //这里&arr[left]要特别注意,不能写成arrdelete[]p;}void merge(ElemType arr[], int left, int right)
{int mid;if (left >= right) return;   //递归出口   这里就不需要递归了mid = ((right - left) >> 1) + left;  //查找到中间值  将数组分成两部分 左区间和右区间//************  归操作  **********//merge(arr, left, mid);         //左区间     merge(arr, mid + 1, right);     //右区1间//************** 并操作 **************//mergeInArr(arr, left, mid, right);  //将归操作后单个有序区间合并}void mergeSort(ElemType arr[], int len)
{merge(arr, 0, len - 1);//归并排序
}

函数说明
(1)使用以下函数需要加头文件#include<memory.h>
(2)memset 函数:内存逐字节赋值,有三个参数,第一个参数是哪个内存,第二个参数赋什么值,第三个参数这个内存需要赋多大的内存。
(3)memcpy内存拷贝函数,有3个参数,表示把第二个参数的首地址里面的内容拷贝到第一个参数表示的首地址里面,拷贝大小为第三个参数表示的大小

8、堆排序

堆的插入就是——每次插入都是将新数据放在数组最后,而从这个新数据的父结点到根结点必定是一个有序的数列,因此只要将这个新数据插入到这个有序数列中即可。

堆的删除就是——堆的删除就是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点开始将一个数据在有序数列中进行“下沉”。

因此,堆的插入和删除非常类似直接插入排序,只不是在二叉树上进行插入过程。所以可以将堆排序形容为“树上插”

更加详细的过程可以查看博文:堆排序算法(图解详细流程)

    //堆排序public static void heapSort(int[] arr) {//构造大根堆heapInsert(arr);int size = arr.length;while (size > 1) {//固定最大值swap(arr, 0, size - 1);size--;//构造大根堆heapify(arr, 0, size);}}//构造大根堆(通过新插入的数上升)public static void heapInsert(int[] arr) {for (int i = 0; i < arr.length; i++) {//当前插入的索引int currentIndex = i;//父结点索引int fatherIndex = (currentIndex - 1) / 2;//如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点//然后继续和上面的父结点值比较,直到不大于父结点,则退出循环while (arr[currentIndex] > arr[fatherIndex]) {//交换当前结点与父结点的值swap(arr, currentIndex, fatherIndex);//将当前索引指向父索引currentIndex = fatherIndex;//重新计算当前索引的父索引fatherIndex = (currentIndex - 1) / 2;}}}//将剩余的数构造成大根堆(通过顶端的数下降)public static void heapify(int[] arr, int index, int size) {int left = 2 * index + 1;int right = 2 * index + 2;while (left < size) {int largestIndex;//判断孩子中较大的值的索引(要确保右孩子在size范围之内)if (arr[left] < arr[right] && right < size) {largestIndex = right;} else {largestIndex = left;}//比较父结点的值与孩子中较大的值,并确定最大值的索引if (arr[index] > arr[largestIndex]) {largestIndex = index;}//如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环if (index == largestIndex) {break;}//父结点不是最大值,与孩子中较大的值交换swap(arr, largestIndex, index);//将索引指向孩子中较大的值的索引index = largestIndex;//重新计算交换之后的孩子的索引left = 2 * index + 1;right = 2 * index + 2;}}//交换数组中两个元素的值public static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}

三、总结

此处使用More Windows图解来对上述的算法进行总结,如果对图解中算法的含义不是很理解可以参考博文:各个排序算法的时间复杂度和稳定性,快排的原理

排序图表

More Windows图解七种经典的排序算法

常用排序算法总结(C语言版)相关推荐

  1. c语言动画原理,动画详解十大经典排序算法(C语言版)

    排序算法是程序员必备的基础知识,弄明白它们的原理和实现很有必要.本文中将通过非常细节的动画展示出算法的原理,配合代码更容易理解. 概述 由于待排序的元素数量不同,使得排序过程中涉及的存储器不同,可将排 ...

  2. 小白学六大排序算法(C语言版)

    今天在csdn刷blink的时候,刷到一个冒泡排序的吐槽,看了看评论区,发现不少人分不清选择排序和冒泡排序,笔者想了一下,发现这两个排序确实相似度蛮高的,所以总结了c语言里涉及到六个排序算法,笔者才疏 ...

  3. 我们一起来排序——使用Java语言优雅地实现常用排序算法

    破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...

  4. 用c语言编写插入排序算法,C语言实现常用排序算法——插入排序

    插入排序是最基础的排序算法,原理: 首先1个元素肯定是有序的,所以插入排序从第二个元素开始遍历: 内循环首先请求一个空间保存待插入元素,从当前元素向数组起始位置反向遍历: 当发现有大于待插入元素的元素 ...

  5. 十种经典排序算法精粹(c语言版本)

    下面给出这段时间我苦心研究验证过的十种经典排序算法的C语言版本,即下面的排序算法: 插入排序,shell排序,冒泡排序,快速排序,选择排序,堆排序,归并排序,桶排序,基数排序和计数排序.整理出来以作备 ...

  6. Unity3D教程:手游开发常用排序算法 -下

    五.堆排序(Heap Sort) 1. 基本思想: 堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小 ...

  7. 数据结构-排序算法(c语言实现篇)

    数据结构-排序算法(c语言实现篇) 排序算法是非常常用的算法,从介绍排序的基本概念,到介绍各种排序算法的思想.实现方式以及效率分析.最后比较各种算法的优劣性和稳定性. 1 排序的概念及应用 1.1 排 ...

  8. 常用排序算法对比(时间复杂度、稳定性)

    常用排序算法对比

  9. 视觉直观感受7种常用排序算法

    视觉直观感受若干常用排序算法 1 快速排序 介绍: 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状 ...

  10. C++STL常用排序算法

    C++STL常用排序算法 学习目标 算法简介 sort 功能描述 函数原型 示例 总结 random_shuffle 功能描述 函数原型 示例 总结 merge 功能描述 函数原型 示例 总结 rev ...

最新文章

  1. 讲真,上班路上 1 小时算很幸福了!
  2. C:/WINDOWS/system32/x 病毒分析和解决建议
  3. laravel框架总结(十一) -- 集合
  4. ubuntu18.04安装nextcloud以及相关配置
  5. SQL(Oracle)日常使用与不常使用函数的汇总
  6. 监听edittext中文字个数变化··
  7. Fragment使用--文章集锦
  8. 关于Java类加载双亲委派机制的思考(附面试题)
  9. 机器学习实战 基于_机器学习实战:基于Scikit-Learn和TensorFlow—第四章笔记
  10. jenkins执行本地/远程shell脚本
  11. 图灵机器人调用数据恢复_python:用itchat打造属于自己的微信聊天机器人,24小时运行...
  12. DNS寻址以及IP解析
  13. 区块链中心化业务必须基于中心化平台吗?
  14. 1、什么是ASIC芯片?
  15. QT颜色对照表,RGB颜色对照表
  16. 毁三观:妻妹把我压身下
  17. 计算机机房主机有哪些功能,机房动环监控主机作用
  18. 【esp32-adf】按键服务源码分析
  19. java 生成随机md5_Java常用工具类(计算MD5,验证码随机生成,天数差值计算)
  20. 【整理】EFI/UEFI BIOS 入门 : All For Beginners

热门文章

  1. 阿里巴巴马云关于创新
  2. 一阶RC滤波器,数字滤波器
  3. catia设置技巧教程:进入管理模式VS工程图模板(如何进入管理模式)
  4. 炫!迪奥元宇宙首展、著名建筑师马岩松元宇宙首作落地百度希壤
  5. 实现在Node环境与浏览器环境下生成二维码
  6. JavaScript 正则表达式的学习
  7. POS打印机系列之 = 网口
  8. 深度学习原理-----逻辑回归算法
  9. 锂电池放空后充不进电_常见锂电池充不进去电怎么办
  10. 悄然发力,大数据为京东插上二次加速的翅膀