各算法比较
图片名词解释:
n: 数据规模
k: “桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
1.冒泡排序
/**
* 冒泡排序:
* 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列;
* 一次比较两个元素,如果它们的顺序错误就把它们交换过来;
* 走访数列的工作是重复的进行,直到没有再需要交换的元素,也就是说该数列已经排序完成。
* @param sortArray
* @return
*/
public static int[] bubbleSort(int[] sortArray){
if(sortArray == null || sortArray.length<2){
return sortArray;
}
for(int i=0;i<sortArray.length;i++){
//每一趟排序从头开始两两比较进行交换,将最大或者最小的元素放到数组末尾已经有序的序列中,需要排序的总趟数等于数组的长度
boolean is_swap = false;//标志本趟排序中是否发生了交换,若没有发生交换则说明序列已经有序,可以直接结束排序
for(int j=0;j<sortArray.length-1-i;j++){
//每次从头开始进行两两比较,第i趟排序则表示数组后面的i个元素已经有序,所以j<sortArray.length-1-i
if(sortArray[j]>sortArray[j+1]){
//每趟排序将最大的元素放到数组末尾,也就是升序排序
int temp = sortArray[j];
sortArray[j] = sortArray[j+1];
sortArray[j+1] = temp;
is_swap = true;
}
}
if(!is_swap){
//本趟排序没有发生元素交换则整个序列已经有序,直接结束
break;
}
}
return sortArray;
}
2.选择排序
/**
* 选择排序:
* 选择排序是一种简单直观的排序算法。
* 首先在未排序序列中找到最小(大)元素,存放到已排序序列的起始位置;
* 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
* 以此类推,直到所有元素均排序完毕。
* @param sortArray
* @return
*/
public static int[] selectionSort(int[] sortArray){
if(sortArray == null || sortArray.length<2){
return sortArray;
}
for(int i=0;i<sortArray.length;i++){
//每一趟排序寻找一个最小的元素放到数组开头已经有序的序列中,排序的总趟数为数组的长度
int minIndex = i;//本趟排序寻找的的最小元素的索引,默认为i,i表示本趟排序最终寻找的最小元素应该插入的位置
for(int j=i+1;j<sortArray.length;j++){
//第i趟排序说明数组开头的i个元素已经有序,所以寻找最小的元素时从第i+1个元素开始
if(sortArray[j]<sortArray[minIndex]){
//元素的值比当前最小元素的值小时,更新本趟排序寻找的最小元素索引
minIndex = j;
}
}
if(minIndex!=i){
//如果最小元素的索引不是i则交换索引为minIndex和i的两个元素
int temp = sortArray[i];
sortArray[i] = sortArray[minIndex];
sortArray[minIndex] = temp;
}
}
return sortArray;
}
3.插入排序
/**
* 插入排序:
* 插入排序是一种简单直观的排序算法。
* 它是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入,
* 在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
* @param sortArray
* @return
*/
public static int[] insertionSort(int[] sortArray){
if(sortArray == null || sortArray.length<2){
return sortArray;
}
for(int i=0;i<sortArray.length-1;i++){
//假设数组第一个元素是已排序序列,其余元素是未排序序列,所以需要扫描(或者说排序)的总次数是未排序序列的元素个数,即:sortArray.length-1。因此i应该小于sortArray.length-1
//i表示已排序序列的最后一个元素的索引,每次需要从i开始向前扫描
int index = i;//在已经排序的序列中扫描的索引,初始为已排序序列的最后一个元素的索引
int temp = sortArray[index+1];//未排序序列的第一个元素索引,随着排序的进行,未排序序列中的元素将会逐渐插入到已排序序列中,所以未排序序列会越来越短,即:未排序序列的第一个元素索引将会逐渐增大直到数组末尾
while(index>=0 && temp<sortArray[index]){
//在已排序序列中从最后一个元素开始向前扫描,需要反复把已排序元素逐步向后挪位,直到找到最新元素应该插入的位置,然后结束
sortArray[index+1] = sortArray[index];
index--;
}
sortArray[index+1] = temp;//程序执行到这里已经找到了最新元素的插入位置:index+1,将最新元素temp插入该位置
}
return sortArray;
}
4.快速排序
/**
* 快速排序:
* 通过一趟排序将待排记录按照一个基准值分隔成独立的两部分,其中一部分记录的值均比另一部分的值小
* 对这两部分记录继续进行快速排序,以达到整个序列有序。
* @param sortArray
* @param start
* @param end
* @return
*/
public static int[] quickSort(int[] sortArray,int start,int end){
if(sortArray == null || sortArray.length<2){
return sortArray;
}
int index = partition(sortArray, start, end);
if(index>start){
quickSort(sortArray, start, index-1);
}
if(index<end){
quickSort(sortArray, index+1, end);
}
return sortArray;
}
/**
* 对待排序数组的指定范围按照基准值分隔成两个部分,其中一部分的值均比另一部分的值小,并且返回分隔基准值的索引位置
* @param sortArray
* @param start 开始范围
* @param end 结束范围
* @return
*/
public static int partition(int[] sortArray,int start,int end){
int benchmark_index = start+(int)((end-start+1)*Math.random());//选取的基准值索引,随机选取一个
int index = start;//分隔成的两个部分中左边部分的索引
swapArrayElement(sortArray, benchmark_index, end);//将基准值放到最后一个
for(int i=start;i<=end;i++){
//循环遍历进行分隔,只需要将所有比基准值小的元素放到基准值的左边部分,那么右边部分就是比基准值大的所有元素
if(sortArray[i]<=sortArray[end]){
//sortArray[end]就是基准值
//当前元素比基准值小,则需要将当前元素放到左边部分,
//当前元素等于基准值则表示已经分隔完毕,此时index指向的位置应该是基准值所在的位置,此时需要将基准值放到此位置,通过下面的交换来完成
if(i>index){
//当前元素比基准值小并且当前元素的索引值比左边部分的索引大,则需要交换元素到左边部分,左边部分的范围应该是start——>index
//交换数组中两个索引对应的元素值
swapArrayElement(sortArray, index, i);
}
if(i<end){
//增加左边部分的索引,若i=end则表示index当前是基准值所在的位置,不需要再自增
index++;
}
}
}
return index;
}
/**
* 交换数组中两个元素的值
* @param array
* @param i
* @param j
*/
public static void swapArrayElement(int[] array,int i,int j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
5.希尔排序
/**
* 希尔排序:
* 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
* 希尔排序是把记录按一定增量分组(例如:增量为5,那么索引为0,5,10,15....的这些元素为一组),对每组使用直接插入排序算法排序;
* 随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,所有元素被分成一组,算法便终止。
* @param sortArray
* @return
*/
public static int[] heerSort(int[] sortArray){
if(sortArray == null || sortArray.length<2){
return sortArray;
}
int increment = sortArray.length/2;//增量
while(increment>0){
for(int i=increment;i<sortArray.length;i++){
//这里和直接插入排序区别在于:每次向前扫描和向后挪位的间隔元素由1个变为增量increment个
int index = i-increment;
int temp = sortArray[i];
while(index>=0 && temp<sortArray[index]){
sortArray[index+increment] = sortArray[index];
index -= increment;
}
sortArray[index+increment] = temp;
}
increment /= 2;//增量每次缩小一半
}
return sortArray;
}
6.计数排序
/**
* 计数排序:
* 计数排序使用一个额外的数组C对应存放待排序数组中每个元素的频数(出现的次数)。
* 然后根据数组C来将待排序数组的元素排到正确的位置,它只能对整数进行排序。
* @param sortArray
* @return
*/
public static int[] countingSort(int[] sortArray){
if(sortArray == null || sortArray.length<2){
return sortArray;
}
int min = sortArray[0];//待排数组最小值
int max = sortArray[0];//待排数组最大值
for(int i=0;i<sortArray.length;i++){
//查找最小值和最大值
if(sortArray[i]<min){
min = sortArray[i];
}
if