排序算法,说真的,就是一个排列顺序的算法,有些废话了哈,不过话说回来,顺序对于我们实现某些功能有很大的帮助,然而如何能够快速的将一个无序数列排列好呢?这次整理了几种排序方法,总结讲述了一下实现的原理以及过程,话比较直接,相信各位看官看代码也许比我说要好得多,直接开始吧。
1.归并排序
单单看这个名字,归并,归并什么呢,当然是归并我们要排列的数列,或者某个含有顺序的内容串,那为什么要归并呢,因为他们分开了呀,为什么分开了呀?简单一点说就是为了方便比较以及处理。如果我们能理解好这个过程,其是归并排序我们也就大体了解了大部分内容了,只不过我们需要将两个过程细化,用代码实现,来上代码:
public class MergeSort {
public static void mergeSort(int[] array, int left, int right){
// 此递归过程的出口为,right - left < 2 时,此时相当于两者之间仅有
// 一个元素
if(right - left < 2) return ;
//计算mid值
int mid = (left + right) >> 1;
//归并的“分开”过程,相当于不停的对左半边的数据进行对半分开
mergeSort(array,left,mid);
//归并的“分开”过程,相当于不停的对右半边的数据进行对半分开
mergeSort(array,mid , right);
//归并的合并过程,将分开的排序合并
merge(array,left,mid,right);
}
private static void merge(int[] array, int left, int mid, int right) {
//相关位置2:此位置 i 初始值为 left的值,j 的初始值为mid的值
int i = left, j = mid, k = 0;
int [] temp = new int [right - left];
//相关位置1,在这里我们是令i < mid, j < right;注意,我们这里
//使用的是<,不是<=,所以当使用<的时候,也就是说i最大到i - 1
//j 最大到right - 1
/**
* 此部分进行排序输出到temp数组中,相当于用mid将整个数组分为
* 两个数组,从两个数组中分别选取符合条件的元素,插入到
* temp数组中
*/
for(; i < mid && j < right; k++){
if(array[i] < array[j]){
temp[k] = array[i++];
}else{
temp[k] = array[j++];
}
}
//可以看到上面的for循环只要有一个到达末尾就停止了,所以,
//分成的两个数组大小不一样时,就容易产生这样的情况,所以
//需要将剩余的部分输入到temp结果数组中
for(; i < mid; k++){
temp[k] = array[i++];
}
for(; j < right; k++){
temp[k] = array[j++];
}
//将某次排序的结果重新拷贝回原来的数组
for (i = left, k = 0; i < right; i++){
array[i] = temp[k++];
}
}
/**
* 打印函数
* @param array 被打印数组
*/
public static void print(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.println("element : " + array[i]);
}
}
}
分析一下可以看到,每次排序所花费的事件为lgn,所以此方法排序所用的时间的O(nlgn)。对于归并排序来说重要的内容为归并和关键位置,即边界的准确确定,这样能够保证算法的基本功能能够实现,其次控制好递归,理解递归所表述的过程,这样就能顺利的理解归并排序了。参考的有些资料说,某些情况下,我们可以结合插入排序达到排序速度提高的目的,当然前提是归并排序中将数组划分到一定范围内时,此时若采用插入排序,效果会比上面的归并排序算法要好,那我们怎么设计呢,仔细思考一下其是还是有思路的,我们要替换的为元素个数小于一个特定值时,我们采用插入排序,其是对应的为止为我们的递归的出口,也就是说当我们划分到小于这个特定值时,就要执行插入排序了,同时,还要保证归并的时候其他部分继续使用merge,所以我们进行如下设计:
//阈值,当元素数量小于此值时,将进行insert排序
private static final int INSECTION_SORT_THERHOLD = 20;
/**
*merge 部分不变,修改mergesort部分
*/
public static void mergeSort_mix_isnertion(int[] array, int left, int right){
// 此递归过程的出口为,right - left < 2 时,此时相当于两者之间仅有
// 一个元素
if(right - left < 2) return ;
if(right - left < INSECTION_SORT_THERHOLD){
insertion_sort(array,left,right);
}else{
//计算mid值
int mid = (left + right) >> 1;
//归并的“分开”过程,相当于不停的对左半边的数据进行对半分开
mergeSort(array,left,mid);
//归并的“分开”过程,相当于不停的对右半边的数据进行对半分开
mergeSort(array,mid , right);
//归并的合并过程,将分开的排序合并
merge(array,left,mid,right);
}
}
/**
*insertion排序方法
*/
public static void insertion_sort(int[] array, int left, int right){
int i, key;
for(int j = left + 1; j < right; j++){
key = array[j];
i = j - 1;
while (i >= left && array[i] > key){
array[i + 1] = array[i];
i = i - 1;
}
array[i + 1] = key;
}
}
测试结果如下:
public static void main(String[] args){
int numItems = 10000000;
int[] array1 = new int[numItems];
int[] array2 = new int[numItems];
int i = 37;
for (i = 37; i != 0; i = (i + 37) % numItems) {
array1[i] = i;
array2[i] = i;
}
long starttime2 = System.currentTimeMillis();
//在这里说一下关于array.length和array.length - 1区别:在这里的赋值
//其实和后面调用的函数有关,有关的位置已经标识。
mergeSort(array2,0,array2.length);
long endtime2 = System.currentTimeMillis();
System.out.println("mergeSort used time" +(endtime2 - starttime2));
long starttime = System.currentTimeMillis();
mergeSort_mix_isnertion(array1,0,array1.length );
long endtime = System.currentTimeMillis();
System.out.println("mergeSort_mix_insection used time" + (endtime - starttime));
}
}
在这里随机生成了array1和array2两个数组,都有100000000个数字,并分别用两个方法进行排序,将阈值设置为20时,运行时间分别为:
单位为ms,可以看到,混合插入排序的算法相比原来的算法有一定的提升,所以有些时候,分析数据以及数据输入特征,以及输入的内容是有必要的,分析可以让自己设计出更为高效的代码完成工作。