几种常见排序算法的基本介绍,性能分析,和c语言实现
本文介绍6种常见的排序算法,以及他们的原理,性能分析和c语言实现:
为了能够条理清楚,本文所有的算法和解释全部按照升序排序进行
首先准备一个元素无序的数组arr[],数组的长度为length,一个交换函数swap,
在main函数中实现排序函数的调用,并输出排序结果:
void swap(int*x , int*y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int arr[] = { 1,8,5,7,4,6,2,3};
int length = sizeof(arr) / sizeof(int);
sort(arr, length);
for (int i = 0;i < length;i++) {
printf("%d\n", arr[i]);
}
return 0;
}
插入排序
第一次循环:
第二次循环:
第三次循环:
外层循环每执行一次就从无序区向有序区中插入一个数据arr[i]
里层循环控制插入的数据arr[i]与其前一个数据比较,如果比前一个数据小,就让前一个数据后移1位
...不断重复上述步骤,直到找到不比arr[i]小的数据arr[j],因为arr[j]后面的数据都后移了1位,所以直接将arr[i]放在空闲的arr[j+1]位置
c程序实现:
void CRsort(int arr[], int length) {
int temp;
for (int i = 0;i < length;i++) {
temp = arr[i];
for (int j = i - 1;j >= 0;j--) {
if (arr[j] > temp) {
arr[j + 1] = arr[j];
}
else {
arr[j + 1] = temp;
break;
}
}
}
}
性能分析
稳定性 : 稳定
-->内层循环执行时,只有遇到大于arr[i]的才会后移,等于arr[i]的不会后移
时间复杂度 : (最坏n²,最好n,平均n²)
-->数据量为n的情况下,外层循环执行n次,内层循环最多执行n次,如果是数据是有序的内层循环只会执行1次
数据越有序,插入排序的执行效率就越高
空间复杂度: 1
-->程序没有用到递归,临时变量不占用存储资源,因此空间复杂度为1
希尔排序
希尔排序又叫"缩小增量排序",是插入排序的一种改进排本:
插入排序优点是适合处理接近有序的元素,缺点是每次只能比较一个元素,希尔排序利用了这两个特点:
区别于普通的插入排序,希尔排序有一个增量序列r,r的初始值一般取 int(length/2),也就是元素数量除以2向下取整
将所有相隔r个单位的元素组成一组,在组内完成排序
增量每次折半,直到最后一次排序时,增量为1,这时元素一定是有序的
最外层循环控制增量组,即每次循环的增量
里面的两层循环就是一个最基本的插入排序,只不过每次加入有序组的不是arr[i+1],而是arr[i+增量]
c程序实现:
void ShellSort(int arr[], int length) {
int r, temp, j;
for (r = length / ;r >= ;r = r / ) {
for (int i = r;i < length;i++) {
temp = arr[i];
j = i - r;
while (j >= && temp < arr[j]) {
arr[j + r] = arr[j];
j = j - r;
}
arr[j + r] = temp;
}
}
}
性能分析
稳定性 : 不稳定
-->每个元素都是在自己的排序组中排序,两个值相同的元素出于不同的排序组中他们总体的相对位置可能会发生变化
时间复杂度 : (最坏n²,最好n,平均n^1.3)
-->希尔排序的分析是一个复杂的问题,以为它的时间是所取“增量”序列的函数,这涉及到一些数学上尚未解决的难题,摘自网上.
最好和普通插入排序相同,如果是数据是有序的内层循环只会执行1次
同样拥有插入排序的特点:数据越有序,它的执行效率就越高
空间复杂度: 1
-->程序没有用到递归,临时变量不占用存储资源,因此空间复杂度为1
冒泡排序
冒泡排序
外层循环每次都将一个元素置入有序区
内层循环控制置入的元素:设置一个指针j,拿arr[j]的值与arr[j+1]进行比较:
如果arr[j]<arr[j+1],就将arr[j]的值与arr[j+1]交换,否则j++,直到无序区比较完毕
值得一提的是,冒泡排序可以进行这样的优化:
设置一个状态码change,当有数据交换发生时change置1
如果一整次排序都没有交换发生,那么这组数据就是有序的,可以直接结束循环
c程序实现:
int MPsort(int arr[], int length) {
int temp, change = 0;
for (int i = 0;i < length;i++) {
for (int j = 0;j < length - i - 1;j++) {
if (arr[j] > arr[j + 1]) {
swap(&arr[j],&arr[j+1]);
change = 1;
}
}
if (change == 0) {
return 0;
}
}
}
性能分析
稳定性 : 稳定
-->数据只有在大于或小于时才会交换,相等时不会交换,因此相同数据的相对位置不会发生改变
时间复杂度 : (最坏n²,最好n,平均n²)
-->在数据完全有序时,不会有数据交换,根据上面的优化处理,状态码change不会改变,外层循环执行一次就结束,时间复杂度为n+1,也就是n.
而如果是非常无序的数据,外层循环执行满n次,时间复杂度就是n²
空间复杂度: log(2)(N)
-->程序没有用到递归,临时变量不占用存储资源,因此空间复杂度为1
快速排序
快速排序是一种效率比较高的排序方法,这张图片我认为介绍的很清楚,搬运来用一下,出处在文章下面:
c程序实现:
void KSSort(int arr[], int left, int right) {
if (left < right) {
int key = arr[left];
int l = left;
int r = right;
while (l < r) {
while (l < r && arr[r] >= key) {
r--;
}
if (l < r) {
arr[l] = arr[r];
l++;
}
else {
break;
}
while (l < r && arr[l] <= key) {
l++;
}
if (l < r) {
arr[r] = arr[l];
r--;
}
else {
break;
}
}
arr[l] = key;
KSSort(arr, left, l - 1);
KSSort(arr, l + 1, right);
}
}
性能分析
稳定性 : 不稳定
--> 假设是稳定的,举个反例:
5 | 3 1 2 | 9 7 8 9 | 4 6 3
这时遍历unvisited部分 刚到了4 (array[8])
显然4<5 ,这是4应该从 unvisited 部分去到 lower 部分。 因此 higher部分第一个元素 9 (array[4]) 和 4互换。变成了这样:
5 | 3 1 2 4 | 7 8 9 9 | 6 3
时间复杂度 : (最坏n²,最好nlogn,平均nlogn)
-->快速排序最差的情况就是每次取到的基准数baes都是排序组的边界值(不是最小的就是最大的),这时外层循环要遍历n次才能将所有数据比较完,时间复杂度就是n²
这种情况多发生在排好序(或接近排好序)的数据中,要在这种数据中避免使用快速排序算法
最好的情况是基准数每次都能取到接近排序组的中位数,用最短的循环次数将程序完全分割,n个数据每次减半,也就是log(2)(N)次后数据被完全分割,算法的时间复杂度为
最差的情况不容易取到,平均时间复杂度取nlogn
空间复杂度: logn
-->在我这个程序里没有用到递归,空间复杂度为1,当然也可以使用递归实现,递归log(2)(N)次,空间复杂度为logn
最后附一张各种排序算法比较图
选择排序
选择排序是最简单的排序方式,也是比较低效的一种排序方式:
第一次循环 :
第二次循环:
第三次循环:
外层循环每执行一次向有序区添加一个元素
内层循环遍历到最大的元素,与刚刚填入有序区的元素交换
......直到数据全部加入有序区
c程序实现:
void XZsort(int arr[] , int length) {
int check;
for (int i = ;i < length - ;i++) {
check = i;
for (int j = i + ;j < length;j++) {
if (arr[j] < arr[check]) {
check = j;
}
}
if (i != check) {
swap(&arr[i],&arr[check]);
}
}
}
性能分析
稳定性 : 稳定
-->存在两个相同的元素时,肯定是下标小的元素先进入有序区,而且在值相等的情况下有序区元素不会被替换
时间复杂度 : (最坏n²,最好n²,平均n²)
-->外层循环要执行n次,才能将n个数据全部加入有序区
无论数据是否有序,内层循环都要将所有的数据比较一遍,找到最小的元素
空间复杂度: 1
-->程序没有用到递归,临时变量不占用存储资源,因此空间复杂度为1
堆排序
堆排序是选择排序的改进版本,它比堆排序的性能要高很多.
要实现堆排序,首先要了解这几个知识点:
大根堆:每个节点的值都大于他的左右子树(小根堆反之)
完全二叉树:除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。
在n个节点的完全二叉树中,叶子节点有(n+1)/2个,非叶子节点有(n-1)/2个
在堆中,下标为n的数的左子树为2n+1,右子树为2n+2
首先要能够将一组无序的数排列成大根堆,大根堆的构造方法如下:
...
外层循环从最后一个非叶子节点( length/2-1 )向根节点遍历,每遍历到一个数据,就执行下面操作:
设置一个根指针i,指向当前要操作树的根节点
比较该树的左右子树的值,将biger指针指向较大的子树
如果该子树的值比根节点还大(arr[biger]>arr[i]),将该子树的值与根节点交换,并将i指针指向该子树,使之成为新的根节点
......递归执行上一步操作,直到有根节点不小于左右子树为止
构造出大根堆之后,每次取整个堆的根节点(也就是第一个元素)存入有序区,将堆的最后一个元素作为根节点
剩下的元素继续构造大根堆,直到数据完全存入有序区
c程序实现:
void MkHeap(int arr[], int i, int length) {
int bigger = * i + ;
int temp;
if (bigger<length){
if (arr[bigger] < arr[bigger + ]) {
bigger++;
}
if (arr[i]<arr[bigger]){//如果子树比爹树大:把子树的值与爹树值交换,并让该子树成为新的爹树
swap(&arr[i], &arr[bigger]);
MkHeap(arr,bigger,length);
}
}
}
void HeapSort(int arr[], int length) {
//从最后一个非叶子节点往根找
for (int i = length / - ;i >= ;i--) {
MkHeap(arr, i, length);
}
for (int j = length - ;j > ;j--) {
swap(&arr[j],&arr[]);
MkHeap(arr, , j-);
}
}
性能分析
稳定性 : 不稳定
-->先假设稳定,然后举个反例:
{6,7,7}这棵树左右子树的值都是7,本来左子树是应该是在前面的,但是6加入有序区之后右子树的7会被提到最上面,这样他们的顺序就被调换了
时间复杂度 : (最坏nlogn,最好nlogn,平均nlogn <底数可以写2也可以不写,根据时间复杂度的性质无论底数是几结果都是一样的> )
-->根据性质,外层循环会执行n次(n代表排序的元素个数),将所有数据全部加入有序区.
而内层递归函数指针从0到n,每次增长前一个的2n+1,忽略掉常数1,也就是执行log(2)(N)次
因此为Nlog(2)(N)次,忽略掉底数2,时间复杂度为nlogn
插入/希尔排序适合接近有序的数据,而堆排序适合非常无序的数据,因为无论数据多么杂乱,希尔排序的时间复杂度都是nlogn
(不放心再说一下 : log(2)(N)是log以2为底n的对数 , 不是log2乘n , 因为底数打不出来只能这样写)
空间复杂度: log(2)(N)
-->这里程序递归了log(2)(N)次,每次递归都占用内存,因此空间复杂度为log(2)(N)
当然也可以使用循环的方式实现,但是那样写出来结构略显混乱,不如递归方式清晰
不稳定算法口诀:快些选队
参考资料(文中的部分图片和思想来自以上材料):
1. 《新编数据结构习题与解析》
2. 文章
https://www.cnblogs.com/skywang12345/p/3603935.html
https://www.cnblogs.com/jingmoxukong/p/4302891.html
http://www.sohu.com/a/341037266_115128
https://www.toutiao.com/a6593273307280179715/?iid=6593273307280179715
3. 关于快速排序算法的稳定性? - 知遥其实是德鲁伊的回答:https://www.zhihu.com/question/45929062/answer/262452296
几种常见排序算法的基本介绍,性能分析,和c语言实现的更多相关文章
- JavaScript版几种常见排序算法
今天发现一篇文章讲“JavaScript版几种常见排序算法”,看着不错,推荐一下原文:http://www.w3cfuns.com/blog-5456021-5404137.html 算法描述: * ...
- Java的几种常见排序算法
一.所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法.排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面. ...
- Java中几种常见排序算法
日常操作中常见的排序方法有:冒泡排序.快速排序.选择排序.插入排序.希尔排序等. 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数 ...
- 几种常见排序算法之Java实现(插入排序、希尔排序、冒泡排序、快速排序、选择排序、归并排序)
排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列. 稳定度(稳定性)一个排序算法是稳定的,就是当有两个相等记录的关 ...
- 几种常见排序算法的C++描述
基本的排序算法有如下特点: 1.几种容易的算法都是以O(N2)排序的 2.Shell排序编程简单,其也是以O(N2)排序的,在实践中用的很多 3.复杂的排序算法往往都是按照O(NlogN)尽心排序的 ...
- 几种常见排序算法的java实现
一.几种常见的排序算法性能比較 排序算法 最好时间 平均时间 最坏时间 辅助内存 稳定性 备注 简单选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定 n小时较好 直接插入排序 O( ...
- php几种常见排序算法
<?php //从时间上来看,快速排序和归并排序在时间上比较有优势,//但是也比不上sort排序,归并排序比较占用内存! $arr = [4,6,1,2,3,89,56,34,56,23,65] ...
- 【知了堂学习笔记】java 编写几种常见排序算法2
排序的分类: 1.直接选择排序 它的基本思想是:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,....,第i次从R[i-1]~ ...
- 【知了堂学习笔记】java 编写几种常见排序算法
排序的分类: 一.交换排序 所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动. 1.冒泡 ...
随机推荐
- 阿里工程师开发了一款免费工具,提升Kubernetes应用开发效率
对于使用了Kubernetes作为应用运行环境的开发者而言,在同一个集群中我们可以使用命名空间(Namespace)快速创建多套隔离环境,在相同命名空间下,服务间使用Service的内部DNS域名进行 ...
- golang数据类型三
- vuxdemo1
//main.js import Vue from 'vue' import router from './router' import store from './store' import axi ...
- 小爬爬4.协程基本用法&&多任务异步协程爬虫示例(大数据量)
1.测试学习 (2)单线程: from time import sleep import time def request(url): print('正在请求:',url) sleep() print ...
- 寒哥教你学iOS - 经验漫谈
http://www.jianshu.com/p/cb54054d3add 寒哥教你学iOS - 经验漫谈 字数2848 阅读1896 评论19 喜欢43 顺便来个广告 iOS开发者 群1734993 ...
- ps学习教程
1.通道抠图:http://www.16xx8.com/photoshop/tongdaokoutu/
- 破解fireworks_cs6、phoneshop_cs6、dreamweaver_cs6
我的Adobe密码是绿尘枫加**0,首字母大写,在我的百度云盘有这三款软件的补丁,这三款软件安装和破解的方式都一样.先下载正常安装好正版软件>正常试用一遍之后,fireworks的补丁装错了文件 ...
- 2017 ACM/ICPC Asia Regional Shenyang Online:number number number hdu 6198【矩阵快速幂】
Problem Description We define a sequence F: ⋅ F0=0,F1=1;⋅ Fn=Fn−1+Fn−2 (n≥2). Give you an integer k, ...
- du,df区别
1.记住命令 du:disk Usage -h, --human-readable print sizes in human readable format df:disk free 2.区别 du ...
- iOS tableView优化
iOS: Autolayout和UITableViewCell的动态高度 http://www.mgenware.com/blog/?p=507 优化UITableViewCell高度计算的那些事 h ...