排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列。

稳定度(稳定性)
一个排序算法是稳定的,就是当有两个相等记录的关键字R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前。

排序算法分类

常见的有插入(插入排序/希尔排序)、交换(冒泡排序/快速排序)、选择(选择排序)、合并(归并排序)等。

一.插入排序

插入排序(Insertion Sort),它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

  1. 从第一个元素开始,该元素可以认为已经被排序。
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
  5. 将新元素插入到该位置后。
  6. 重复步骤2~5。
    public static void insertionSort(int[] data) {
for (int index = 1; index < data.length; index++) {
int key = data[index];
int position = index;
// shift larger values to the right
while (position > 0 && data[position - 1] > key) {
data[position] = data[position - 1];
position--;
}
data[position] = key;
}
}

二.希尔排序

希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  1. 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率。
  2. 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。
    static <E extends Comparable<? super E>> void shellSort(List<E> a) {
int h = 1;
while (h < a.size()/3) h = h*3 + 1; // <O(n^(3/2)) by Knuth,1973>: 1, 4, 13, 40, 121, ...
for (; h >= 1; h /= 3)
for (int i = h; i < a.size(); i++)
for (int j = i; j >= h && a.get(j).compareTo(a.get(j-h)) < 0; j-=h)
Collections.swap(a, j, j-h);
}

三.冒泡排序

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

冒泡排序算法的运作如下:

  1. 比较相邻的元素,如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
    public static void bubbleSort(int[] data) {
int temp = 0;
for (int i = data.length - 1; i > 0; --i) {
boolean isSort = false;
for (int j = 0; j < i; ++j) {
if (data[j + 1] < data[j]) {
temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
isSort = true;
}
} // 如果一次内循环中发生了交换,那么继续比较;如果一次内循环中没发生任何交换,则认为已经排序好了。
if (!isSort)
break;
}
}

四.快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

步骤为:

  1. 从数列中挑出一个元素,称为 "基准"(pivot)。
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
/*
* more efficient implements for quicksort. <br />
* use left, center and right median value (@see #median()) for the pivot, and
* the more efficient inner loop for the core of the algorithm.
*/
public class Quicksort { public static final int CUTOFF = 11; /**
* quick sort algorithm. <br />
*
* @param arr an array of Comparable items. <br />
*/
public static <T extends Comparable<? super T>> void quicksort(T[] arr) {
quickSort(arr, 0, arr.length - 1);
} /**
* get the median of the left, center and right. <br />
* order these and hide the pivot by put it the end of of the array. <br />
*
* @param arr an array of Comparable items. <br />
* @param left the most-left index of the subarray. <br />
* @param right the most-right index of the subarray.<br />
* @return T
*/
public static <T extends Comparable<? super T>> T median(T[] arr, int left, int right) { int center = (left + right) / 2; if (arr[left].compareTo(arr[center]) > 0)
swapRef(arr, left, center);
if (arr[left].compareTo(arr[right]) > 0)
swapRef(arr, left, right);
if (arr[center].compareTo(arr[right]) > 0)
swapRef(arr, center, right); swapRef(arr, center, right - 1);
return arr[right - 1];
} /**
* internal method to sort the array with quick sort algorithm. <br />
*
* @param arr an array of Comparable Items. <br />
* @param left the left-most index of the subarray. <br />
* @param right the right-most index of the subarray. <br />
*/
private static <T extends Comparable<? super T>> void quickSort(T[] arr, int left, int right) {
if (left + CUTOFF <= right) {
// find the pivot
T pivot = median(arr, left, right); // start partitioning
int i = left, j = right - 1;
for (;;) {
while (arr[++i].compareTo(pivot) < 0);
while (arr[--j].compareTo(pivot) > 0);
if (i < j)
swapRef(arr, i, j);
else
break;
} // swap the pivot reference back to the small collection.
swapRef(arr, i, right - 1); quickSort(arr, left, i - 1); // sort the small collection.
quickSort(arr, i + 1, right); // sort the large collection. } else {
// if the total number is less than CUTOFF we use insertion sort
// instead (cause it much more efficient).
insertionSort(arr, left, right);
}
} /**
* method to swap references in an array.<br />
*
* @param arr an array of Objects. <br />
* @param idx1 the index of the first element. <br />
* @param idx2 the index of the second element. <br />
*/
public static <T> void swapRef(T[] arr, int idx1, int idx2) {
T tmp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = tmp;
} /**
* method to sort an subarray from start to end with insertion sort
* algorithm. <br />
*
* @param arr an array of Comparable items. <br />
* @param start the begining position. <br />
* @param end the end position. <br />
*/
public static <T extends Comparable<? super T>> void insertionSort(T[] arr, int start, int end) {
int i;
for (int j = start + 1; j <= end; j++) {
T tmp = arr[j];
for (i = j; i > start && tmp.compareTo(arr[i - 1]) < 0; i--) {
arr[i] = arr[i - 1];
}
arr[i] = tmp;
}
} private static void printArray(Integer[] c) {
for (int i = 0; i < c.length; i++)
System.out.print(c[i] + ","); System.out.println();
} public static void main(String[] args) {
Integer[] data = {10, 4, 9, 23, 1, 45, 27, 5, 2}; System.out.println("bubbleSort...");
printArray(data);
quicksort(data);
printArray(data);
}
}

五.选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

因为每一趟确定元素的过程中都会有一个选择最小值的子流程,所以人们形象地称之为选择排序。

举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

    public static void selectSort(int[] data) {
int minIndex = 0;
int temp = 0;
for (int i = 0; i < data.length; i++) {
minIndex = i; // 无序区的最小数据数组下标
for (int j = i + 1; j < data.length; j++) { // 在无序区中找到最小数据并保存其数组下标
if (data[j] < data[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) { // 如果不是无序区的最小值位置不是默认的第一个数据,则交换之。
temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
}
}

六.归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并操作的过程如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列。
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置。
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置。
  4. 重复步骤3直到某一指针达到序列尾。
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。
    public static int[] mergeSort(int[] arr) {// 归并排序 --递归
if (arr.length == 1) {
return arr;
}
int half = arr.length / 2;
int[] arr1 = new int[half];
int[] arr2 = new int[arr.length - half];
System.arraycopy(arr, 0, arr1, 0, arr1.length);
System.arraycopy(arr, half, arr2, 0, arr2.length);
arr1 = mergeSort(arr1);
arr2 = mergeSort(arr2);
return mergeSortSub(arr1, arr2);
} private static int[] mergeSortSub(int[] arr1, int[] arr2) {// 归并排序子程序
int[] result = new int[arr1.length + arr2.length];
int i = 0;
int j = 0;
int k = 0;
while (true) {
if (arr1[i] < arr2[j]) {
result[k] = arr1[i];
if (++i > arr1.length - 1) {
break;
}
} else {
result[k] = arr2[j];
if (++j > arr2.length - 1) {
break;
}
}
k++;
}
for (; i < arr1.length; i++) {
result[++k] = arr1[i];
}
for (; j < arr2.length; j++) {
result[++k] = arr2[j];
}
return result;
}

完整代码(除QuickSort)

package com.clzhang.sample.thinking;

import java.util.*;

/**
* 几路常见的排序算法Java实现
* @author acer
*
*/
public class CommonSort {
/**
* 插入排序具体算法描述如下:
* 1.从第一个元素开始,该元素可以认为已经被排序
* 2.取出下一个元素,在已经排序的元素序列中从后向前扫描
* 3.如果该元素(已排序)大于新元素,将该元素移到下一位置
* 4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
* 5.将新元素插入到该位置后
* 6.重复步骤2~5
*/
public static void insertionSort(int[] data) {
for (int index = 1; index < data.length; index++) {
int key = data[index];
int position = index;
// shift larger values to the right
while (position > 0 && data[position - 1] > key) {
data[position] = data[position - 1];
position--;
}
data[position] = key;
}
} /**
* 希尔排序,算法实现思想参考维基百科;适合大数量排序操作。
*/
static <E extends Comparable<? super E>> void shellSort(List<E> a) {
int h = 1;
while (h < a.size()/3) h = h*3 + 1; // <O(n^(3/2)) by Knuth,1973>: 1, 4, 13, 40, 121, ...
for (; h >= 1; h /= 3)
for (int i = h; i < a.size(); i++)
for (int j = i; j >= h && a.get(j).compareTo(a.get(j-h)) < 0; j-=h)
Collections.swap(a, j, j-h);
} /**
* 冒泡排序算法的运作如下:
* 1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
* 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
* 3.针对所有的元素重复以上的步骤,除了最后一个。
* 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。[1]
*/
public static void bubbleSort(int[] data) {
int temp = 0;
for (int i = data.length - 1; i > 0; --i) {
boolean isSort = false;
for (int j = 0; j < i; ++j) {
if (data[j + 1] < data[j]) {
temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
isSort = true;
}
} // 如果一次内循环中发生了交换,那么继续比较;如果一次内循环中没发生任何交换,则认为已经排序好了。
if (!isSort)
break;
}
} /**
* 选择排序的基本思想是:
* 1.遍历数组的过程中,以 i 代表当前需要排序的序号,则需要在剩余的 [i+1…n-1] 中找出其中的最小值,
* 2.然后将找到的最小值与 i 指向的值进行交换。
* 因为每一趟确定元素的过程中都会有一个选择最小值的子流程,所以人们形象地称之为选择排序。
* @param data
*/
public static void selectSort(int[] data) {
int minIndex = 0;
int temp = 0;
for (int i = 0; i < data.length; i++) {
minIndex = i; // 无序区的最小数据数组下标
for (int j = i + 1; j < data.length; j++) { // 在无序区中找到最小数据并保存其数组下标
if (data[j] < data[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) { // 如果不是无序区的最小值位置不是默认的第一个数据,则交换之。
temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
}
} /**
* 归并操作的过程如下:
* 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
* 2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
* 3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
* 4.重复步骤3直到某一指针达到序列尾
* 5.将另一序列剩下的所有元素直接复制到合并序列尾
*/
public static int[] mergeSort(int[] arr) {// 归并排序 --递归
if (arr.length == 1) {
return arr;
}
int half = arr.length / 2;
int[] arr1 = new int[half];
int[] arr2 = new int[arr.length - half];
System.arraycopy(arr, 0, arr1, 0, arr1.length);
System.arraycopy(arr, half, arr2, 0, arr2.length);
arr1 = mergeSort(arr1);
arr2 = mergeSort(arr2);
return mergeSortSub(arr1, arr2);
} private static int[] mergeSortSub(int[] arr1, int[] arr2) {// 归并排序子程序
int[] result = new int[arr1.length + arr2.length];
int i = 0;
int j = 0;
int k = 0;
while (true) {
if (arr1[i] < arr2[j]) {
result[k] = arr1[i];
if (++i > arr1.length - 1) {
break;
}
} else {
result[k] = arr2[j];
if (++j > arr2.length - 1) {
break;
}
}
k++;
}
for (; i < arr1.length; i++) {
result[++k] = arr1[i];
}
for (; j < arr2.length; j++) {
result[++k] = arr2[j];
}
return result;
} private static void printArray(int[] c) {
for (int i = 0; i < c.length; i++)
System.out.print(c[i] + ","); System.out.println();
} public static void main(String []args){
int[] data = {10,4,9,23,1,45,27,5,2}; System.out.println("bubbleSort...");
int[] a = data.clone();
printArray(a);
bubbleSort(a);
printArray(a); System.out.println("selectSort...");
int[] b = data.clone();
printArray(b);
selectSort(b);
printArray(b); System.out.println("insertionSort...");
int[] c = data.clone();
printArray(c);
insertionSort(c);
printArray(c); System.out.println("shellSort...");
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<data.length;i++)
list.add(data[i]);
System.out.println(list);
shellSort(list);
System.out.println(list); System.out.println("mergeSort...");
int[] d = data.clone();
printArray(d);
printArray(mergeSort(d));
} }

本文相关引用:

http://baike.baidu.com/

http://zh.wikipedia.org/

最新文章

  1. (八)Eclipse创建Maven项目运行mvn命令
  2. 查看apache、linux、kernel、nginx等版本
  3. Consistent hashing —— 一致性哈希
  4. 剑指offer 判断树是不是对称的
  5. java在的数据类型
  6. Python3 多线程的两种实现方式
  7. 使用ng-options指令创建下拉框
  8. 简单的C语言猜数字小游戏
  9. linux下redis单机版搭建
  10. R语言实战(二)——数据分析基础知识
  11. 【洛谷p1258】小车问题
  12. nvm use 指定版本后无效 win7
  13. 【EF框架】另一个 SqlParameterCollection 中已包含 SqlParameter。
  14. python 将16进制转化为2进制
  15. POJ 1122.FDNY to the Rescue! Dijkstra
  16. Implementation:Binary Indexed Tree 树状数组
  17. linux 学习第十五天(vsftpd配置)
  18. PHPStrom 里修改Emmet对php的自动扩展
  19. nginx模块记录
  20. LNMP的搭建与原理

热门文章

  1. [android错误] requires API level *
  2. 【转】TCP/IP详解学习笔记(二)
  3. java实现内部排序算法
  4. 公众号 - 解决所有测试中的CORS问题
  5. Hibernate(十四)缓存
  6. 选择合适的Linux版本
  7. spring boot 1.5.2 操作mongodb3.4.0
  8. UVa 10642 - Can You Solve It?
  9. SVN如何查看修改的文件记录
  10. ubuntu开机自动启动xampp/lampp的两种方法