比较一览
排序法 最优时间 平均复杂度 最差情形 稳定度 额外空间 备注 类型 选择 O(n 2 ) O(n2) O(n2) 不稳定 O(1) n小时较好 选择排序 冒泡 0-O(n) O(n2) O(n2) 稳定 O(1) n小时较好 交换排序 插入 O( n ) O(n2) O(n2) 稳定 O(1) 大部分已排序时 插入排序 Shell O( n ) 依赖步长串行 O(ns) 1<s<2 不稳定 O(n) s是所选分组 插入排序 快速 O(nlogn) O(nlogn) O(n2) 不稳定 O(nlogn) n大时较好 交换排序 归并 O( n ) O(nlogn) O(nlogn) 稳定 O(1)-O(n) n大时较好 归并排序 堆 O(nlogn) O(nlogn) O(nlogn) 不稳定 O(1)-O(n) n大时较好 选择排序
1. 快速排序
介绍:
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。
步骤:
- 从数列中挑出一个元素,称为 “基准”(Pivot),
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
代码:
public class QuitSort {
static int cpNum = 0;
static int swapNum = 0;
public static void main(String[] args) {
int[] a = {5,44,3,64,3,64,34,45,4,112};
QuitSort.quitSort(a,0,a.length-1);
System.out.println("数组大小:"+a.length+" 比较次数:"+cpNum+" 交换次数:"+swapNum);
for (int i : a) {
System.out.print(i+"--");
}
}
public static void quitSort(int[] a,int start ,int end) {
if (start >= end )
return;
int i = start;
int j = end;
int key = a[start];
while (i < j) {
for (; j > i; j--) {
cpNum++;
if (a[j] < key) {
swapNum++;
a[i] = a[j];
break;
}
}
for (; i < j; i++) {
cpNum++;
if (a[i] > key) {
swapNum++;
a[j] = a[i];
break;
}
}
}
a [i] = key;
quitSort(a,start,i-1);//sort left
quitSort(a,i+1,end);//sort left [
}
}
2. 归并排序
介绍:
归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用
步骤:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤3直到某一指针达到序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
代码:
import java.util.Arrays;
/**
* 归并排序
*
* 空间换时间
* Cworst(n) = O(n log n)
* @author stephen
*
*/
public class MergeSort {
/**
* @param args
*/
public static void main(String[] args) {
int[] a = {5,4,3,2,1};
//int[] a = { 5, 44, 9, 64, 3, 64, 34, 45, 3, 112 };
sort(a);
System.out.println("比较次数:" + cpNum);
System.out.println("移动次数:" + moveNum);
for (int i : a) {
System.out.print(i + "--");
}
}
static int cpNum = 0;
static int moveNum = 0;
public static void sort(int[] a) {
int t;
int length = a.length;
int middle = (length) / 2;
if (length > 1) {
int[] b = Arrays.copyOfRange(a, 0, middle);
int[] c = Arrays.copyOfRange(a, middle, length);
moveNum += length;
sort(b);
sort(c);
merge(a, b, c);
}
}
private static void merge(int[] a, int[] b, int[] c) {
int i = 0, j = 0, k = 0;
while (i < b.length && j < c.length) {
cpNum++;
if (b[i] > c[j]) {
a[k] = c[j];
k++;
j++;
moveNum++;
} else {
a[k] = b[i];
k++;
i++;
moveNum++;
}
}
if (i == b.length) {
while (j < c.length) {
a[k] = c[j];
j++;
k++;
moveNum++;
}
} else {
while (i < b.length) {
a[k] = b[i];
i++;
k++;
moveNum++;
}
}
}
}
3. 堆排序
介绍:
堆积排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
步骤:
(比较复杂,略)
4. 选择排序
介绍:
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
代码:
/**
* C(c) = n*(n-1)/2 O(n^2)
* @author stephenluu
*
*/
public class SelectionSort {
public static void main(String[] args) {
// int[] a = {5,4,3,2,1};
int[] a = { 5, 44, 9, 64, 3, 64, 34, 45, 3, 112 };
selectionSort(a);
System.out.println("比较次数:" + cpNum);
System.out.println("移动次数:" + swapNum);
for (int i : a) {
System.out.print(i + "--");
}
}
static int cpNum = 0;
static int swapNum = 0;
public static void selectionSort (int[] a){
int t;
for (int i = 0; i < a.length; i++) {
for (int j = i+1 ; j < a.length; j++) {
cpNum++;
if (a[i] > a[j]){
swapNum++;
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
}
};
}
5. 冒泡排序
介绍:
冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
步骤:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码:
/**
* 冒泡排序
* key 相邻
* 跌带出最大的数放到最后
* @author stephen
*
*/
public class BubbleSort {
/**
* @param args
*/
public static void main(String[] args) {
//int[] a = {1,2,3,4,5};
int[] a = {5,44,3,64,3,64,34,45,4,112};
//sort(a);
sortImproved(a);
System.out.println("比较次数:"+n);
for (int i : a) {
System.out.print(i+"--");
}
}
static int n = 0;
//C(n) = n(n-1)/2 Sworst(n) = n(n-1)/2 -- O(n^2)
public static void sort(int[] a){
int t;
for (int i = a.length-1; i >0; i--) {
for (int j = 0; j <i ; j++) {
if (a[j] > a[j+1])
{
t = a[j+1];
a[j+1] = a[j];
a[j] = t;
}
n++;
}
}
}
//Cmin(n) = n-1 Sworst(n) = n(n-1)/2 -- O(n^2)
public static void sortImproved(int[] a){
int t;
for (int i = a.length-1; i >0; i--) {
boolean isSwap = false;
for (int j = 0; j <i ; j++) {
if (a[j] > a[j+1])
{
t = a[j+1];
a[j+1] = a[j];
a[j] = t;
isSwap = true;
}
n++; //计算次数
}
if (!isSwap)
return;
}
}
}
6. 插入排序
介绍:
插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
步骤:
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置中
- 重复步骤2
代码:
/**
* Cworst(n) = (n-1)n/2 : O(n^2) Cavg = (n^2)/4 : O(n^2)
*
* @author stephenluu
*
*/
public class InsertionSort {
public static void main(String[] args) {
// int[] a = {5,4,3,2,1};
int[] a = { 5, 44, 9, 64, 3, 64, 34, 45, 3, 112 };
insertionSort(a);
System.out.println("比较次数:" + cpNum);
for (int i : a) {
System.out.print(i + "--");
}
}
static int cpNum = 0;
public static void insertionSort(int[] a) {
int t;
int j;
for (int i = 1; i < a.length; i++) {
t = a[i];
j = i - 1;
while (j >= 0 && a[j] > t) {
cpNum++;
a[j + 1] = a[j];
j--;
}
a[j + 1] = t;
}
}
}
7. 希尔排序
介绍:
希尔排序,也称递减增量排序算法,是插入排序的一种高速的改进版本。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>
代码:
这是D.Shell 的实现版本,不是我写的
int shellsortSh(int p[], int n) {
int op = 0;
int h, i, j, temp;
for (h = n / 2; h > 0; h = h / 2) {
for (i = h; i < n; i++) {
temp = p[i];
for (j = i - h; j >= 0 && p[j] > temp; j -= h) {
p[j + h] = p[j];
op++;
}
p[j + h] = temp;
op++;
}
}
return op;
}