九大排序算法

http://www.cnblogs.com/xwdreamer/archive/2012/04/06/2435123.html

http://www.cnblogs.com/biyeymyhjob/archive/2012/07/17/2591457.htmlphp

 

http://www.cnblogs.com/codingmylife/archive/2012/10/21/2732980.htmlhtml


 

冒泡排序:最好状况需比较n-1次,最坏状况需比较n(n-1)/2;
选择排序:最好状况需比较n(n-1)/2,最坏状况需比较n(n-1)/2;
对分排序:最好状况需比较n/2logn,最坏状况需比较近似nlogn;

 

1.排序算法简要比较

  冒泡排序java

名称 数据对象 稳定性 时间复杂度 空间复杂度 描述
平均 最坏
插入排序 数组、链表 O(n^2) O(1) (有序区,无序区)。把无序区的第一个元素插入到有序区的合适的位置。对数组:比较得少,换得多。
直接选择排序 数组 × O(n^2) O(1) (有序区,无序区)。在无序区里找一个最小的元素跟在有序区的后面。 对数组:比较得多,换得少。
链表
堆排序 数组 × O(nlogn) O(1) (最大堆,有序区)。从堆顶把根卸出来放在有序区以前,再恢复堆。
归并排序 数组、链表 O(nlogn) O(n) +O(logn) , 若是不是从下到上 把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。可从上到下或从下到上进行。
快速排序 数组 × O(nlogn) O(n^2) O(logn) ,O(n) (小数,枢纽元,大数)。
Accum qsort 链表 O(nlogn) O(n^2) O(logn) ,O(n) (无序区,有序区)。把无序区分为(小数,枢纽元,大数),从后到前压入有序区。
   
决策树排序   O(logn!) O(n!) O(n) <O(logn!) <O(nlogn)
   
计数排序 数组、链表 O(n) O(n+m) 统计小于等于该元素值的元素的个数 i,因而该元素就放在目标数组的索引 i位。(i≥0)
桶排序 数组、链表 O(n) O(m) 将值为 i 的元素放入i 号桶,最后依次把桶里的元素倒出来。
基数排序 数组、链表     一种多关键字的排序算法,可用桶排序实现。
  • 均按从小到大排列
  • n 表明数据规模
  • m 表明数据的最大值减最小值

2.冒泡排序:

冒泡排序基本思路:

  冒泡排序的主要思想就是经过一趟排序,将数组中的最大值转移到的数组最后一个元素。一个有n个元素的数组,那么通过(n-1)趟排序之后,就可以完成排序。由于若是(n-1)个元素的位置肯定了,那么最后的那一个元素的位置也就肯定了。还有须要注意的是每一趟排序都能肯定数组中的一个元素位置,所以通过一趟排序之后,须要进行排序比较的元素就减小一个。c++

  在每一趟排序过程当中,都是从数组元素第一个开始,依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。而后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(由于可能因为第2个数和第3个数的交换,使得第1个数再也不小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已是最大的),第二趟结束,在倒数第二的位置上获得一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。因为在排序过程当中老是小数往前放,大数日后放,至关于气泡往上升,因此称做冒泡排序。算法

冒泡排序的改进

冒泡排序法存在的不足及改进方法:windows

  第一,在排序过程当中,执行完当前的第k趟排序后,可能数据已所有排序完备,可是程序没法判断是否完成排序,会继续执行剩下的(n-1-k)趟排序。为了解决这一不足,能够设置一个标志位flag,将其初始值设置为非0,表示被排序的数组是一个无序的数组。每一次排序开始前设置flag值为0,表示假设已经完成排序,若是这一趟排序过程当中都没有数据发生交换,则flag的值就为0;可是当这一趟排序过程当中存在数据交换时,修改flag为非0,表示这一趟仍然有数据没有完成排序。在新一轮排序开始时,检查此标志,若此标志为0,表示上一次没有作过交换数据,则表示通过上一趟的排序,已经完成了对数组的排序,排序结束;不然继续进行排序;数组

根据上述思路进行改进的冒泡排序代码实现以下:ide

View Code

   第二,当排序的数据比较多时排序的时间会明显延长。改进方法:快速排序:具体作法:任意选取某一记录(一般取第一个记录),比较其关键字与全部记录的关键字,并将关键字比它小的记录所有放在它的前面,将比它大的记录均存放在它的后面,这样,通过一次排序以后,可将全部记录以该记录所在的分界点分为两部分,而后分别对这两部分进行快速排序,直至排序完。也就是说,利用快速排序过程当中的Partition()方法返回的index将须要排序的元素分为两段,在index前面的数据都比index位置的数据小,在index后面的元素都比index位置的数据大。而后对先后两段数据进行冒泡排序,这样缩小的冒泡排序的数据量。函数

  局部冒泡排序算法对冒泡排序的改进:性能

  在冒泡排序中,一趟扫描有可能无数据交换,也有可能有一次或屡次数据交换,在传统的冒泡排序算法及近年来的一些改进的算法中,只记录一趟扫描有无数据交换的信息,对数据交换发生的位置信息则不予处理。为了充分利用这一信息,能够在一趟全局扫描中,对每一反序数据对进行局部冒泡排序处理,称之为局部冒泡排序。局部冒泡排序与冒泡排序算法具备相同的时间复杂度,而且在正序和逆序的状况下,所需的关键字的比较次数和移动次数彻底相同。因为局部冒泡排序和冒泡排序的数据移动次数老是相同的,而局部冒泡排序所需关键字的比较次数常少于冒泡排序,这意味着局部冒泡排序极可能在平均比较次数上对冒泡排序有所改进,当比较次数较少的优势不足以抵消其程序复杂度所带来的额外开销,而当数据量较大时,局部冒泡排序的时间性能则明显优于冒泡排序。

3.快速排序

 参考前面写的博文:

快速排序算法QuickSort

快速排序算法QuickSort(二)

冒泡排序与快速排序代码实现

View Code

4.堆排序

堆排序的主要思路:

  1. 通常状况下都使用数组实现堆的结构,以大顶堆为例,由于是使用数组,元素下表从0开始,因此要求arry[i]>arry[i*2+1],arry[i]>arry[i*2+2]。表示父结点大于其左右孩子结点的值。
  2. 因此堆排序的第一步是根据其实数组元素,构建一个堆。构建堆就须要调整堆中的元素,从第一个非叶子结点开始调整,第一个非叶子结点的位置是t=(len/2-1)。由于堆是一棵满二叉树的结构,因此从0到t的全部结点都是非叶子结点。
  3. 在调整堆结构过程当中,咱们须要比较父节点与其左右子结点的值,能够先求出左右子结点的最大值,即c=max(arry[leftChild],arry[rightChild]),而后将这个最大值与父节点arry[parent]进行比较,若是父节点小于子节点,那么交换父节点与子节点的值(子节点中较大的那个元素),若是父节点大与子节点,则代表知足堆结构要求,不须要交换。在调整的过程当中可能会出现一种状况,好比父节点A与子节点B进行交换,此时A结点是B结点的子节点,可是A结点又是CD两个结点的父节点,所以咱们还须要调整ACD三个元素位置,一直调整到结点没有子节点为止。
  4. 在堆排序中,首先是构建一个最大堆,此处须要用到buildHeap(int arry[],int len)这个函数,而这个函数中又调用了adjustHeap(int arry[],int parent,int len),因此能够将build当作是由adjustheap组成的。在构建完成之后,咱们交换首元素与末元素的位置,交换之后咱们知道首元素不符合最大堆的定义,因此须要调整首元素的位置,也就是adjustHeap(arry,0,len-i-1)。
  5. 咱们能够发现adjustHeap(int arry[],int parent,int len)的时间复杂读是o(logn),而须要调用o(n)次这个方法啊,因此时间复杂度时o(nlogn)

堆排序的代码实现

c++实现

View Code

java实现(ps:2012-10-8)

View Code

此处使用异或方法交换两个元素的值存在一些bug,建议仍是使用常规的交换元素方法。

5.归并排序

参考文献:白话经典算法系列之五 归并排序的实现

5.1归并排序思路

  1. 归并排序是创建在归并操做上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个很是典型的应用。分治的思想相似于快速排序,不过它比快速排序多了一步merge。首先考虑下如何将将二个有序数组A/B合并的问题。这个很是简单,设定两个指针分别指向A和B的首地址,而后比较这两个指针所指向的数,哪个数小就将其保存到临时数组T中,而后指针往前移动一个位置。好比指针pa指向数组A的首地址,pb指向数组B的首地址,指针pt指向临时数组的首地址。加入pa指向的数小于pb指向的数,则将pa所指向的数存放在pt所指的位置上,而后pa和pt都往前移动一格。重复上述操做,直到pa和pb有一个指向数组的末尾元素。此时将另一个指针没有指向末尾元素的数组中的元素所有都保存到临时数组中。这样就完成了两个有序数组的归并操做。
  2. 解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将一个数组分红二组,分为A和B,若是这二组组内的数据都是有序的,那么就能够很方便的将这二组数据进行归并。那么如何让这二组组内数据有序了?能够将A,B组各自再分红二组。依次类推,当分出来的小组只有一个数据时,能够认为这个小组组内已经达到了有序,而后再合并相邻的二个小组就能够了。这样经过先递归的分解数列,再合并数列就完成了归并排序。

5.2代码示例

View Code

注意点:

在这里咱们看到咱们merge的分段是arry[start...mid]跟arry[mid+1...end],咱们不能分红arry[start...mid-1]跟arry[mid...end],这是由于mid=(start+end)/2,假如start=1,end=2,则mid=1,此时调用MergeSort(arry,start=1,end=3,temp);会就变成了MergeSort(arry,mid=1,end=3,temp)的死循环了。

相关文章
相关标签/搜索