常见的排序算法简介

排序的稳定性

因为待排序的记录序列中可能存在两个或两个以上的关键字相等的记录, 排序结果可能会存在不唯一的情况。所以就有稳定与不稳定的定义。

假设ki=kj( 1 =< i <= n,1 =< j <= n, i != j),且在排序前的序列中ri领先于rj。如果排序后ri仍领先于rj,则称所用的排序方法是稳定的;反之,若可能使得排序后的序列中rj领先于ri,则称所用的排序方法是不稳定的。

只要有一组关键字发生类似情况,就可认为此排序方法是不稳定的。

内排序和外排序

根据在排序过程中待排序记录是否全部放在内存中,排序分为内排序和外排序。

内排序是在排序整个过程中,待排序的所有记录全部被放置在内存中。

外排序是由于排序的记录个数太多,不能同时放置在内存中,整个排序过程需要在内外存之间多次交换数据才能进行。

对内排序来说,排序算法的性能主要有3个影响因素:

1、时间性能

排序算法的时间开销是衡量其好坏的最重要的标志。

在内排序中,主要进行两种操作:比较和移动。

高效率的内排序算法应该具有尽可能少的关键字比较次数和尽可能少的记录移动次数。

2、辅助空间

评估算法的另一个主要标准是执行算法所需要的辅助存储空间。

辅助存储空间是除了存放待排序所占用的存储空间外,执行算法所需要的其他存储空间。

3、算法的复杂性

指算法本身的复杂性,过于复杂的算法也会影响排序的性能。

接下来本文介绍各种排序算法。

  1. 冒泡排序Bubble Sort

冒泡排序是一种交换排序,它的基本思想是:

两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

算法复杂度分析:

使用优化后的冒泡排序,最好的情况下,仅需要n - 1次比较,时间复杂度为O(n);最坏情况下,需要n(n - 1)/2次比较和交换;

所以平均时间复杂度为O(n2)。

  1. 简单选择排序Simple Selection Sort

选择排序的基本思想:

每一次遍历时选取关键字最小的记录作为有序序列的第i个记录。

算法复杂度分析

简单选择排序最大的特点就是交换移动数据次数少,但它的比较次数是和数组本身是否有序是无关的,即无论最好最差的情况,都要进行n(n-1)/2次比较;在最好的情况下,不需要进行交换,在最坏的情况下,进行n-1次交换。

所以平均时间复杂度为O(n2)。

  1. 直接插入排序Straight Insertion Sort

直接插入排序的基本操作:

将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录递增1的有序表。

插入排序是进行值移动,而是不值交换。所以在量较小的情况下插入排序性能要优于冒泡和简单选择排序。

算法复杂度分析:

在最好的情况下,只需进行比较n - 1次,无需进行移动;

在最坏的情况下,比较(n + 2)(n - 1)/2次,交换(n + 4)(n - 1)/2次。

所以平均时间复杂度O(n2)

  1. 二分插入排序Binary Insert Sort

二分(折半)插入排序是一种在直接插入排序算法上进行小改动的排序算法。其与直接排序算法最大的区别在于查找插入位置时使用的是二分查找的方式,在速度上有一定提升。

算法复杂度分析:

插入每个记录需要O(log i)比较,最多移动i+1次,最少2次。最佳情况O(n log n),最差和平均情况O(n^2)。

总排序码比较次数比直接插入排序的最差情况好得多,但比最好情况要差,所元素初始序列已经按排序码接近有序时,直接插入排序比二分插入排序比较次数少

  1. 希尔排序Shell Sort

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了。

更好的理解方式

将数组列在一个表中并对行排序(用插入排序)。重复这过程,不过每次用更小的列来进行。最后整个表就只有一列了。

将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size而不是i++)。

比如第一次放在5列中对每行使用快速排序排序,第二次放在3列中,最后放在1列中。类比于步长从5到3再到1。

算法复杂度分析

希尔排序的算法复杂度和增量序列有关,只要最终步长为1任何步长序列都可以工作。可以参加希尔排序。

  1. 堆排序Heap Sort

堆是具有下列性质的完全二叉树:

每个节点的值都大于或等于其左右孩子节点的值,成为大顶堆;

每个节点的值都小于或等于其左右孩子节点的值,成为小顶堆;

完全二叉树性质

按完全二叉树的性质,该树可以被顺序存储在数组中,按不同的角标进行表示。

即:

Parent(i) = (i-1)/2,i 的父节点下标

Left(i) = 2i + 1,i 的左子节点下标

Right(i) = 2(i + 1),i 的右子节点下标

基本思想

将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆定的根节点,将它移走(与堆数组末尾元素交换),再将剩余n-1个序列重新构造成一个堆,这样就会得到第二大值,以此类推,就能得到一个有序序列了。

算法复杂度分析

在构建堆时,对每个非叶子节点来说,最多进行2次比较和互换操作,复杂度为O(n);

在进行排序时,第i次取堆顶记录重新建堆需要用O(log i )时间,并需要取n-1次,所以重建堆的时间为O(nlogn)。

所以堆排序的时间复杂度为O(nlogn)。

实现步骤:

最大堆调整(Max_Heapify):从堆的倒数第一个非叶子节点作调整,使得子节点永远小于父节点。没有必要从叶子节点开始,叶子节点可以看作是已符合堆特点的节点。

创建最大堆(Build_Max_Heap):将堆所有数据重新排序

堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整。

  1. 归并排序Merge Sort

概念:

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的典型应用。

它指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。这里仅对内排序的两路归并方法进行讨论。

算法思路

把 n 个记录看成 n 个长度为 l 的有序子表

进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表

重复第 2 步直到所有记录归并成一个长度为 n 的有序表为止。

算法复杂度分析:

在最后一步,需要依次遍历两个已排序的好的数组,此时的时间复杂度为O(n)。

同时又进行着二路归并,形成一颗完全二叉树,此时整个排序需要进行log2n次。

所以归并排序的时间复杂度为O(nlogn)。这是它的最好、最坏、平均的时间性能。

  1. 快速排序Quick Sort

基本思想

通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分小,则可分别对这两部分记录继续进行排序,直到整个序列有序。

复杂度分析

最好情况:partition每次划分的都很均匀,如果排序n个关键字,其递归树的深度就为floor(log2n)+ 1次,此时的复杂度为O(nlogn)。

如果是最坏情况,每次partition都只操作一个数字,该递归树即为一颗斜树,比较次数为n(n - 1)/2,时间复杂度为O(n2)。

平均复杂度为O(nlogn)。

  1. 桶排序Bucket Sort

基本思想

工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。

步骤

设置一个定量的数组当作空桶子。

寻访序列,并且把项目一个一个放到对应的桶子去。

对每个不是空的桶子进行排序。

从不是空的桶子里把项目再放回原来的序列中。

算法复杂度

对于N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序平均时间复杂度为:

O(N)+O(M(N/M)log(N/M))=O(N+N(logN-logM))=O(N+NlogN-N*logM)

可以看出,最好情况即当N=M时,每个桶只有一个数据时,能够达到O(N)。

  1. 计数排序Count Sort

基本思想

计数排序是一种稳定的线性时间排序算法。

计数排序使用一个额外的数组C,其中C数组的第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。

步骤:

找出待排序的数组中最大和最小的元素

统计数组中每个值为i的元素出现的次数,存入数组 C 的第 i 项

对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)

反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

算法复杂度分析

当输入的元素是n个0到k之间的整数时,它的运行时间是Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。

由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。

原文地址:https://blog.51cto.com/14512197/2442993

时间: 2024-10-07 09:58:15

常见的排序算法简介的相关文章

java几种常见的排序算法总结

[java] view plain copy /*************几种常见的排序算法总结***************************/ package paixu; public class PaiXu { final int MAX=20; int num[]=new int[MAX]; { System.out.print("生成的随机数组是:"); for(int i=0;i<20;i++){ num[i]=(int)(Math.random()*100)

数据结构之常见的排序算法c语言实现

常见的简单排序算法有冒泡排序.选择排序.插入排序.快排.堆排序.归并排序.希尔排序等,这些排序的理论在网上有很多,这就只给出常见的排序算法源码,上学时候写的,不足之处欢迎大家指正. 下面几种排序的主函数入口为:     int main(int argc, char* argv[])         {      int i, len;      int a[] = {8,5,6,4,9,10,3,15,2,17};           len = (sizeof(a) / sizeof(a[0

常见比较排序算法的比较

几种常见的排序算法之比较 排序的基本概念以及其算法的种类,介绍几种常见的排序算法的算法:冒泡排序.选择排序.插入排序.归并排序.快速排序.希尔排序的算法和分析它们各自的复杂度,然后以表格的形式,清晰直观的表现出它们的复杂度的不同.在研究学习了之前几种排序算法的基础上,讨论发现一种新的排序算法,并通过了进一步的探索,找到了新的排序算法较之前几种算法的优势与不足. 排序算法,是计算机编程中的一个常见问题.在日常的数据处理中,面对纷繁的数据,我们也许有成百上千种要求,因此只有当数据经过恰当的排序后,才

常见的排序算法

描述: 排序算法可谓数据结构模块中的重中之重,常见的哈希表,二叉树,搜索树/平衡树,位图等数据结构只是处理实际问题的抽象方法,实际在处理接受或生成的数据集时,排序算法显得尤其重要,排序算法家族很庞大,其中包括了冒泡排序,选择排序,插入排序,堆排序,快速排序,归并排序,基数排序,计数排序,希尔排序,箱排序,树型排序等众多算法,每种排序都有各自的特性,没有好坏之分,只有在特定的场景使用合适的排序算法才是上策,单纯的来比显得太过绝对,没有可比性.因为实际需求及各方面条件的限制使得排序算法的可选范围往往

Python全栈开发之5、几种常见的排序算法以及collections模块提供的数据结构

在面试中,经常会遇到一些考排序算法的题,在这里,我就简单了列举了几种最常见的排序算法供大家学习,说不定以后哪天面试正好用上,文章后半段则介绍一下collections模块,因为这个模块相对于python提供的基本数据结构(list,tuple,dict)不被人们所熟悉,但是如果你对他们了解的话,用起来也是非常方便高效的. 排序算法 一.冒泡排序(BubbleSort) 步骤: 比较相邻的元素,如果第一个比第二个大,就交换他们两个. 循环一遍后,最大的数就“浮”到了列表最后的位置. 将剩下的数再次

学习排序算法简介

学习排序算法简介 学习排序(Learning to Rank, LTR)是一类基于机器学习方法的排序算法. 传统经典的模型,例如基于TFIDF特征的VSM模型,很难融入多种特征,也就是除了TFIDF特征之外,就无法融入其他种类的特征了. 而机器学习的方法很容易融合多种特征,而且有成熟深厚的理论基础,参数也是通过迭代计算出来的,有一套成熟理论来解决稀疏.过拟合等问题. LTR方法大致可以分成三类: 1) Pointwise 单文档方法 2) Pairwise 文档对方法 3) Listwise 文

用Java来写常见的排序算法

随着校招的临近 算法是校招中很重要的一个部分 总结了常见几种排序算法,各种算法的时间复杂度和空间复杂度大家也需要多了解下 package com.huwei.sort; /** * 各种排序算法 * * @author huwei * */ public class Sort { public static void main(String[] args) { int[] a = { 60, 57, 89, 47, 57, 98, 45, 35, 73 }; Sort sort = new So

常见的排序算法--java版

个人总结的常见的排序算法 public class Sort { // 1.冒泡:稳定,最优O(n) 最差O(n^2) 平均O(n^2) private static void sort1(int[] arr) { for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length - 1; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = ar

几种常见的排序算法

1.插入类排序 在一个已经有序的序列中,插入一个新的记录.有直接插入排序.折半插入排序.希尔排序. 插入类排序 直接插入排序 1 void InsertSort(int R[], int n) 2 { 3 int i, j; 4 int temp; 5 for (i = 1; i < n; ++i) 6 { 7 temp = R[i]; 8 j = i - 1; 9 while (j >= 0 && temp < R[j]) 10 { 11 R[j+1] = R[j];