目录

概述

一,选择排序-直接插入排序(Direct insertion sort)

二,插入排序-希尔排序(Shell sort)

三,选择排序-简单选择排序(Simple selection sort)

四,选择排序-堆排序(Heap sort)

五,交换排序-冒泡排序(Bubble Sort)

六,交换排序-快速排序(Quick-Sort)

七,归并排序-(Merge sort)

八, 基数排序(Count Sort)

九,排序算法复杂度及稳定性分析

十,外部排序:


概述:

排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存

这里说的八大排序就是就是内部排序

一,选择排序-直接插入排序(Direct insertion sort)

基本思想:

在一个有序数组里面插入一个数据X,通过遍历比较找到位置,再把后面数据向后移动,把X插入到数组中,数组元素加一(插入单个数据)

(对数组进行排序),设立一个临时变量存储作为临时存储和判断数组边界之用,将序列第一个元素当成是有序的,然后从第2个记录逐个进行插入,直至整个序列有序为止。

如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

void InsertSort(int* a, int n)  //插入排序
{for(int i=0;i<n-1;i++){int tmp=a[i+1];//插入的值int end=i;//比较的值while(end>=0){if(tmp<a[end])//tmp小于当前值,把end所指向的位置空出来,然后把tmp插进去  {a[end+1]=a[end];end--;}else{break;//不小于  跳出}a[end+1]=tmp;//拆入} }}

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2.时间复杂度:O(n^2)(情况最差时,即逆序转有序,最好为O(n));

3.空间复杂度:O(1);

4.稳定

二,插入排序-希尔排序(Shell sort)

希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序

基本思想:

先将整个数组分成若干个子数组,通过对子数组进行排序,达到数组"基本有序",再对整个数组进行插入排序,即可达到有序.

实现方法:

1,先分组,间隔为Gap的数据为一组,然后对这组数据进行排序,再分组,再排,直到数组被分完.

2,然后再把Gap减小,继续分组,排序.

3,此时数组基本有序,然后将最后Gap设为1,即进行直接插入排序,得到有序数组

算法实现:

先简单处理增量序列:增量序列gap=(gap/3+1),gap为要排序的数组数据个数

即:先将要排序的一组记录按某个增量gap(gap/3,gap为要排序数的个数)分成若干组子序列,每组中记录的下标相差gap/3.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(gap/3)对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。

希尔排序可以认为是插入排序的优化

具体代码:

void ShellSort(int* a, int n)//希尔排序 {int gap=n;while(gap>1)//判断是否还需排序{gap=(gap/3+1);//保证最后一次直接插入排序for(int i=0;i<n-gap;++i)//进入循环,对每一组进行排序{int end=i;int tmp=a[end+gap]; while(end>=0){if(tmp<a[end]){a[end+gap]=a[end];end-=gap;}   else{break;}a[end+gap]=tmp;}} }   }

特性:

1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3—N^2)
4. 稳定性:不稳定

三,选择排序-简单选择排序(Simple selection sort)

基本思想:

        每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的
数据元素排完.

实现方法:

1,在数组[0,n]中选取最小的数,

2,交换数据,最小数放在左边,

3,在[1,n-1]再次选取,交换,缩减,直到集合剩一个元素

.

算法实现:

void SelectSort(int*a,int n)
{int left=0;while(left<n){int min=left; for(int i=left;i<n;i++)//找最小值  {if(a[min]>a[i]){min=i;}}Swap(&a[left],&a[min]);//交换数据   然后找次小,交换 left++;   }
} 

特性:

1,容易理解,但是效率太低,实际当中不太使用

2,时间复杂度O(n^2),空间复杂度O(1);

3,不稳定

四,选择排序-堆排序(Heap sort)

基本思想:

堆排序(HeaoSort)是基于数据结构堆设计的一种排序算法,通过堆来选择数据,向上(向下)调整,得到小数(大数),然后再与堆底数据进行交换,即可排序,需要注意的是排升序建大堆,排降序建小堆

实现方法:

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序

1,建大堆,把根交换到最底,然后在减一个元素继续调整

2,向下调整,继续交换,直到最后一个元素

1,交换函数

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}

2,向下调整算法

void AdjustDwon(int* a, int n, int root)
{int parent = root;int child = parent * 2 + 1;//找到孩子while (child < n){if (child + 1 < n && a[child + 1] > a[child])//考虑右孩子越界和判断那个孩子大{++child;}if (a[child] > a[parent])//判断孩子和父亲谁大,孩子大,向上交换{Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

3,建堆和排序

void HeapSort(int* a, int n)
{// 升序  建大堆for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDwon(a, n, i);}int end = n - 1;//向下调整,交换while (end > 0){Swap(&a[0], &a[end]);AdjustDwon(a, end, 0);--end;}
}

特性:

1,堆排序用来选数,效率就高了很多

2,时间复杂度O(n*logn),空间复杂度O(1);

3,不稳定

五,交换排序-冒泡排序(Bubble Sort)

基本思想:

在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。。

实现方法:

代码如下:

//冒泡排序
void Bubblesort(int *a,int n)
{for(int i=0;i<n;i++)//控制交换次数 {for(int j=0;j<n-i-1;j++)//向后冒泡 ,控制边界 {if(a[j]>a[j+1])//如果前一个值大于后一个值,交换. {swap(&a[j],&a[j+1]);}      }}
} 

特性总结:

1,容易理解

2,时间复杂度O(n^2),空间复杂度O(1)

3,稳定

六,交换排序-快速排序(Quick-Sort)

基本思想:

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止,类似于树形结构,每次排序把Key值放到应在的位置.

将区间按照基准值划分为左右两半部分的常见方式有:
1. hoare版本
2. 挖坑法
3. 前后指针版本

一,Hoare版本(左右指针)

左边值设为key,然后右边先走,找小的,比key小,然后左边走找比key大,然后交换左边右边,

代码实现如下(左右指针):

1,左右指针调用

int PartSort1(int* a, int left, int right)
{int keyi = left;while (left < right){// 找小while (left < right && a[right] >= a[keyi])--right;// 找大while (left < right && a[left] <= a[keyi])++left;swap(&a[left], &a[right]);//交换左右值}swap(&a[keyi], &a[left]);//最后交换key与leftreturn left;//返回当前节点,[0,left-1],[left+1,right]递归排序
}

2,递归排序

void QuickSort(int*a,int left,int right)//快排
{if(left>=right){return ;} int key=PartSort1(a, left, right);// [begin, keyi-1] keyi [keyi+1, end]//类似于二叉树 QuickSort(a,left,key-1);//递归对左数组排序 QuickSort(a,key+1,right);//递归对右数组排序
} 

二,挖坑法

其设定key数组第一个值为坑,右边找下,左边找大,找到一个,交换,形成新的坑,最后把key放到坑里

1,挖坑法

int PartSort2(int* a, int left, int right)
{int key=a[left];while(left<right){//找小的 while(left<right&&a[right]>=key){--right; }a[left]=a[right];//找到小值,放到坑里,形成新的坑 while(left<right&&a[left]<=key){++left;}a[right]=a[left];//找到大值,放到坑里,形成新的坑 }a[left]=key;//把key放到数组中属于它的位置,左边所有值小于它,右边所有值大于它, return left; //返回left,分组处理数据
}

2,递归排序

void QuickSort(int*a,int left,int right)//快排
{if(left>=right){return ;} int key=PartSort2(a, left, right);// [begin, keyi-1] keyi [keyi+1, end]//类似于二叉树 QuickSort(a,left,key-1);//递归对左数组排序 QuickSort(a,key+1,right);//递归对右数组排序
} 

三,前后指针法

如图所示

选取key值,cur小于key值,prev++,交换cur与prev值,

1,前后指针法

int PartSort3(int* a, int left, int right)
{int keyi=left;int cur=left+1;int prev=left;while(cur<=right){if(a[cur]<a[keyi]&&++prev!=cur)//判断cur与key的值,并且防止自己与自己交换{swap(&a[cur],&a[prev]);}++cur;}swap(&a[keyi],&a[prev]);return prev;
} 

二,递归调用

void QuickSort(int*a,int left,int right)//快排
{if(left>=right){return ;} int key=PartSort3(a, left, right);// [begin, keyi-1] keyi [keyi+1, end]//类似于二叉树 QuickSort(a,left,key-1);//递归对左数组排序 QuickSort(a,key+1,right);//递归对右数组排序
} 

快排优化

若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录,本质在于防止最坏的情况发生(1,已经有序2,数据全部相等)

为了避免这种情况,选取头尾和中间元素,比较大小,找大小处于中间的元素为key值,实现对快排的优化,时间复杂度仍为O(nlog^n),每次调用排序的时候把key置一下.

实现如下;

//快排三数优化
int  GetMid(int* a, int left, int right)
{int mid = (left + right) >> 1;// left  mid  rightif (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;} }else // a[left] > a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}

四,非递归实现快排

通过使用栈对数据进行排序,递归本身就是一个压栈的过程

void QuickSortNonR(int* a, int begin, int end)
{Stack st;StackInit(&st);StackPush(&st, begin);StackPush(&st, end);while (!StackEmpty(&st)){int left, right;right = StackTop(&st);//弹出数据StackPop(&st);left = StackTop(&st);StackPop(&st);int keyi = PartSort1(a, left, right);if (left < keyi-1)//比较大小,压大的入栈,{StackPush(&st, left);StackPush(&st, keyi-1);}if (keyi+1 < right){StackPush(&st, keyi+1);StackPush(&st, right);}}StackDestroy(&st);
}

特性总结:

1.快速排序整体的综合性能和使用场景都是比较好的

2,时间复杂度:O(N*logN)

3,空间复杂度:O(N*logN)

4,不稳定

七,归并排序-(Merge sort)

基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。通过递归实现对小数组有序,再返回回来,

代码实现:

//排序入口
void MergeSort(int* a, int n)
{int *temparr=(int*)malloc(n*sizeof(int));if(temparr!=NULL){msort(a,temparr,0,n-1);//进行排序free(temparr);}else{printf("malloc fail\n");}} 

递归分组进行排序

void msort(int*a,int*arr,int left,int right)
{//如果只有一个元素,默认有序,只需被归并即可  if(left<right){//找中间点 int mid=(left+right)>>1;//对左半区进行归并 msort(a,arr,left,mid);//对右半区归并 msort(a,arr,mid+1,right);//对已经排序的数组进行合并 merge(a,arr,left,mid,right); }
}

排序

//归并排序
void merge(int*a,int*arr,int left,int mid,int right)
{//标记左半区第一个未排序的元素int l_pos=left; //标记右半区第一个未排序的元素int r_pos=mid+1;//临时数组下标的元素 int pos=left;//合并while(l_pos<=mid&&r_pos<=right){if(a[l_pos]<a[r_pos])arr[pos++]=a[l_pos++];elsearr[pos++]=a[r_pos++];} //合并左半区剩余的元素while(l_pos<=mid){arr[pos++]=a[l_pos++];}//合并右半区剩余的元素while(r_pos<=right){arr[pos++]=a[r_pos++];}//把临时数组合并后的元素复制到a中 while(left<=right){a[left]=arr[left];left++;}
}

非递归实现归并排序

其实现过程就是分治,只不过不用递归返回,而是直接排序,排完以后再粘贴到数组中

代码实现如下:

void MergeSortNoR(int* a, int n)
{int *temparr=(int*)malloc(n*sizeof(int));if(temparr==NULL){printf("malloc fail\n");}int gap=1;while(gap<n){for(int i=0;i<n;i+=2*gap)//分组 {int begin1=i,end1=i+gap-1,begin2=i+gap,end2=i+2*gap-1;// 如果第二个小区间不存在就不需要归并了,结束本次循环if(begin2>=n) break;// 如果第二个小区间存在,但是第二个小区间不够gap个,结束位置越界了,需要修正一下if(end2>=n)end2=n-1;_Merge(a,temparr,begin1,end1,begin2,end2); }gap*=2;     }free(temparr);
} 
//非递归实现
void _Merge(int*a,int*tmpArr,int begin1,int end1,int begin2,int end2)
{//两个半区元素归并int i=begin1;//每次归并的区间左边界 int left=begin1;int right=end2; while(begin1<=end1&&begin2<=end2){if(a[begin1]<a[begin2])tmpArr[i++]=a[begin1++];elsetmpArr[i++]=a[begin2++];} // 合并左半区元素while(begin1<=end1){tmpArr[i++]=a[begin1++];} //合并右半区元素while(begin2<=end2){tmpArr[i++]=a[begin2++]; } //把临时数组的元素复制到a中while(left<=right){a[left]=tmpArr[left];left++;}
} 

特性总结:

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定

八, 基数排序(Count Sort)

基本思想:

1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中

代码如下,加入了优化,避免了空间的浪费

void CountSort(int* a, int n)
{int max = a[0], min = a[0];for (int i = 0; i < n; ++i){if (a[i] > max)max = a[i];if (a[i] < min)min = a[i];}int range = max - min + 1;int* count = malloc(sizeof(int)*range);memset(count, 0, sizeof(int)*range);for (int i = 0; i < n; ++i)//计数 {count[a[i] - min]++;}int i = 0;for (int j = 0; j < range; ++j)//排序 {while (count[j]--){a[i++] = j + min;}}free(count);
}

计数排序的特性总结:
1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
2. 时间复杂度:O(MAX(N,范围))
3. 空间复杂度:O(范围)
4. 稳定性:稳定

排序算法复杂度及稳定性分析

外部排序:

外排序是数据量较大,内存放不下,数据放到外部磁盘空间,一般使用归并排序进行外排序

假设内存为512m,给10亿个数据,然后内存每次读取512m的数据,排序完成后返回给磁盘,然后重复这个过程,直到拍完,然后外部的小文件,再经过归并,即可得到一个有序的数据.

八大排序详解-超详细相关推荐

  1. 八大排序 详解(下)——指向函数的指针 的使用

    <八大排序 详解(上)> <八大排序 详解(中)> 紧接前两篇博文,我们这篇博文所要讲解的是我们前两篇博文编写的所有函数的使用.生成随机数组的函数的讲解以及一种及其凶悍的调用方 ...

  2. Java 泛型详解(超详细的java泛型方法解析)

    Java 泛型详解(超详细的java泛型方法解析) 1. 什么是泛型 泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型.也就是说在泛型使用过程中,操作的数据类型被指定为 ...

  3. 【八大排序详解~C语言版】直接插入排序-希尔排序- 直接选择排序-堆排序-冒泡排序-快速排序-归并排序-计数排序

    八大排序 1.直接插入排序 2.希尔排序 3.直接选择排序 直接选择排序改进 4.堆排序 1.建堆 2.利用堆删除思想来进行排序 5.冒泡排序 6.快速排序 递归实现 非递归实现 7.归并排序 递归实 ...

  4. mysql 联表比对,MySQL联表查询详解/超详细mysql left join,right join,inner join用法分析比较...

    超详细mysql left join,right join,inner join用法分析 下面是例子分析 表A记录如下: aID aNum 1 a20050111 2 a20050112 3 a200 ...

  5. Java八大排序详解及源码

    今天介绍一下java中的八大排序,首先看一下这八种排序之间的关系: 排序目录 1. 直接插入排序 1.1基本思想 1.2实例 1.3实现代码 1.4运行结果 2.希尔排序(最小增量排序) 2.1基本思 ...

  6. log4j 配置详解(超详细)

    一.Log4j简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使 ...

  7. 【bind()函数】JavaScript手写bind()及详解-超详细~~~

    这两天学习了手写call.apply.bind,手写bind思考了很久才实现了MDN的示例的结果,所以记录下来~ 因为是第一篇文章,所以可能存在一些错误,希望各位大佬批评指正,不吝赐教. 也欢迎不懂的 ...

  8. @Autowired注解详解——超详细易懂

    @Autowired详解 要搞明白@Autowired注解就是要了解它是什么?有什么作用?怎么用?为什么? 首先了解一下IOC操作Bean管理,bean管理是指(1)spring创建对象 (2)spr ...

  9. 斜率优化详解(超详细, 有图有代码有注释)

    文章目录 斜率优化引入 从例题开始 斜率优化Part 1: 推为斜率式 斜率优化Part 2: 合法点集的斜率单调性 Part 3: 找到最优决策点 Part 4: 斜率优化大流程 Part 5: 斜 ...

最新文章

  1. C++知识点2——指针、引用基础
  2. cv2.matchTemplate()函数的应用,匹配图片后画出矩形
  3. CSS基础篇--css reset重置样式有那么重要吗?
  4. python3.7基础教程_python 3.7极速入门教程6文件处理
  5. 2017北理计算机考研复试线,北京理工大学2017年硕士研究生复试分数线
  6. 游戏用计算机配置表显卡,5000元电脑配置9代i5配GTX1660TI显卡配置清单(可装Win7)...
  7. PMP 与IPMP区别是什么?
  8. Linux文件系统Ext2,Ext3,Ext4性能大比拼
  9. Hive On Spark搭建报错:Failed to create Spark client for Spark session xx: ..TimeoutException
  10. HM-A300小程序安卓打印异常
  11. IE浏览器http请求缓存问题
  12. 微信小程序头像怎么改变形状_如何实现微信小程序换头像?三步帮你搞定!
  13. 应用特征值矩阵对角方法
  14. 微软:人工智能迅速崛起,面临着严重的技术短缺
  15. 如何将Excel多行单元格文字合并到一个单元格中
  16. 文本 去除重复行(sublime Text3 ,正则表达式)
  17. bzoj3238 [Ahoi2013]差异 后缀自动机
  18. 学Java是自学还是报培训班好?
  19. HTML5篮球弹跳运动规律,打篮球怎么练弹跳力 弹跳力怎么训练
  20. React高级特性之Render Props

热门文章

  1. 好友联盟DOS杀毒盘
  2. laravel安装-中文语言包
  3. python七段数码管绘制英文字母_Python入门基础:七段数码管绘制
  4. 用PS给外景人像添加逆光效果
  5. 镰刀电脑闹钟 V1.0
  6. 手把手带你了解80C51(持续更新)
  7. 基于Java毕业设计养老院管理系统源码+系统+mysql+lw文档+部署软件
  8. React仿大众点评外卖app
  9. 小型水库雨水情测报及大坝安全监测系统
  10. 小黑记事本怎样设置html,小黑记事本便签怎样设置?便签设置流程图文介绍