您的位置:首页 > 编程语言 > Java开发

java 八大排序算法详解

2016-05-20 13:35 369 查看
首先介绍一下排序的概念,排序是这样一个过程,即基于某一标准,要么升序要么降序将某一组项目按照某个规定的顺序排列。在java 中排序基本排序有八中,我习惯按排序算法的效率来分类。

一.算法的时间复杂度和稳定性

顺序排序:它通常使用一对嵌套循环对nn个元素进行排序,大概需要n2n^2次比较,即时间复杂度为O(n2)O(n^2),例如,选择排序,插入排序,冒泡排序。

对数排序:它对nn个元素排序通常需要大约nlog2nnlog_{2^n},即时间复杂度为O(nlog2n)O(nlog_{2^n}),例如,快速排序,归并排序。

希尔排序:时间复杂度为O(n1.3)O(n^{1.3})

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

基数排序:基数排序的时间复杂度为O(n)O(n) 。

从上到下时间复杂度依次降低,即算法的效率依次提高。

算法的稳定性:通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。

其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就 是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。

选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,

冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

二.排序算法的实现

这里将所有的排序方法都写成静态方法,由于使用了泛型,所以所有的静态方法都要使用泛型静态方法,即在 static 后面加上泛型
<T extends Comparable<T>>
,表示要排序的元素必须要Comparable的子类。

public class SortMethod {

/**
* 公用方法
* 数组中两个元素位置对调
* @param data 要操作的数组
* @param index1 要操作的元素的角标
* @param index2 要操作的元素的角标
*/
private static <T extends Comparable<T>> void swap(T[] data,int index1,int index2){
T temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}

//==============================================================
/**
* 选择排序:首先选择改组元素最小的放到第一位,然后选择剩下的元素中最小的放在第二位
* 以此类推
* @param data
*/
public static <T extends Comparable<T>> void selectionSort(T[] data){
for(int index=0;index<data.length;index++){
int scan = index;
for(int i=index+1;i<data.length;i++){
if(data[i].compareTo(data[scan])<0){
scan = i;
}
}
swap(data,index,scan);
}
}

//==============================================================
/**
* 插入排序:通过反复的将某一特定的值插入到该列表某个已排序的子集中,
* 即先将列表前两个元素排序,然后将剩余的元素插入到这个已排序的列表中。
* @param data
*/
public static <T extends Comparable<T>> void insertionSort(T[] data){
//下面称已排序好的元素列表 为 sorted
//index 为sorted 中元素的个数

for(int index = 1;index < data.length;index++){
T key = data[index];//要插入的元素
int position = index;

while(position>0 && data[position-1].compareTo(key)>0){
data[position] = data[position-1];
position--;
}
data[position] = key;
}
}

//==============================================================
/**
* 冒泡排序:通过重复地比较相邻的元素且在必要时将他们互换。
* @param data
*/
public static <T extends Comparable<T>> void bubbleSort(T[] data){

for(int i=0;i<data.length;i++){
for(int index=0;index<data.length-1;index++){
if(data[index].compareTo(data[index+1])>0){
swap(data, index, index+1);
}
}
}
}

//==============================================================
/**
* 快速排序:选取一个元素A,将比A大的放在右边,比A小的放在左边。
* 然后对左右两边的列表在进行相同的处理,直到排序完成。
* @param data
*/
public static <T extends Comparable<T>> void quickSort(T[] data){
quickSort(data, 0, data.length-1);
}

/**
* 递归式的排序方法
* @param data 数组对象
* @param min 列表最小索引
* @param max 列表最大索引
*/
private static <T extends Comparable<T>> void quickSort(T[] data,int min,int max){
if(min<max){
int indexOfPartition = partition(data, min, max);

quickSort(data, min, indexOfPartition-1);

quickSort(data, indexOfPartition+1, max);
}
}

/**
* 将列表分为两个分区,左边分区元素小于 中间值 ,右边分区元素大于中间值,并返回中间值索引
* partition 方法的两个内循环用于寻找位于错误分区的交换元素。第一个循环从左边扫描到右边,
* 以寻找大于分区元素的元素。第二个循环从右边扫描到左边,以寻找小于分区元素的元素。在找
* 到这俩个元素的时候,则将他们互换。该过程会一直继续下去,直至右索引和左索引在该列表的
* “中间”相遇。他们相遇的位置同样也表明了该分区元素最终会驻留在那个地方。
* @param data 数组对象
* @param min 列表最小索引
* @param max 列表最大索引
* @return
*/
private static <T extends Comparable<T>> int partition(T[] data,int min,int max){
T partitionElement ;
int left , right;
int middle = (min+max)/2;
partitionElement = data[middle];//中间元素

swap(data, middle, min);//现将 中间元素移动到最左边,等找到中间位置后在将中间元素换回去

left = min;
right = max;

while(left<right){
//从min开始到,找出比 partitionElement 大的元素
while(left<right && data[left].compareTo(partitionElement)<=0)
left++;
//从max开始,找出比 partitionElement 小的元素
while(data[right].compareTo(partitionElement)>0)
right--;

if(left<right)
swap(data, left, right);
}
swap(data, min, right);

return right;
}
//==============================================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: