排序总结

01

冒泡排序

说到排序首先肯定是众所周知的冒泡排序算法,冒泡排序就是从后向前取数组中的相邻的两个值进行比较,较大的数下称(向后放),较小的数上浮(向前放).
假设这里有一个随机无序数组为[2,1,3],那么在第一次循环时先比较"3"和"1",由于1<3所以1上浮,3下沉,数组不变,继续向前比较"2"和"1",由于2>1,所以2下沉,1上浮,数组变成[1,2,3],第二次循环同第一次循环,只是不再比较第一个数,因为通过第一次循环可以得出第一个数是整个数组中最小的数字.
冒泡算法的好处在于,他的逻辑非常清晰也非常好理解,哪怕理解不了背也是很好背的,但是同时他有很大的缺点,由于它需要遍历数组长度-1次才能确保数组排序,导致它非常的耗时.
我们以上面的30W条数据为例,完成以下代码:
public static void main(String[] args) {
Double[] arr = new Double[300000];
for (int i =0;i<300000;i++){
arr[i] = Math.random();
}
long a = System.currentTimeMillis();
BubbleSort(arr);
long b = System.currentTimeMillis();
System.out.println(b-a);
}
/**
* 冒泡排序
*/
public static void BubbleSort(Double[] arr){
double temp;
for(int i=0; i<arr.length-1; i++){
for(int j=arr.length-1; j>i; j–){
if(arr[j] < arr[j-1]){
temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
}
在排序前和排序后分别赋值了一个时间戳,这二者的差我们可以近似的看为是冒泡排序的耗时.

    作者在自己电脑上运行并打印出耗时为"553232",即为553秒左右,可想而知在大数据量的排序状况下,冒泡排序并不适用.

02

选择排序

    选择排序的原理与冒泡排序类似,实在长度为n的无序数组中,遍历n-1个数,找到其中最小的数与第n个数进行比较和交换.

    现有无序数组[2,1,3],选择排序先遍历后两个数,找到其中最小的数字"1",然后与数组中第一个数比较,发现2>1则进行交换,第一遍循环后的数组为[1,2,3],然后进行第二次交换,此时只有一个数"3",将3与第二个数字"2"比较,发现2<3,则数组不变.

    选择算法原理与冒泡排序类似,那么自然优缺点也差不多,都只是原理很好理解但是遇上大数据量的排序表现很差.选择排序代码如下:

/**
* 选择排序
*/
public static void SelectSort(Double[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
if (min != i) {
double temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
}
同样带入上述30W条数据中进行排序测试,作者电脑输出耗时为:92025,花了92秒左右的时间才完成了数据排序,所以也不建议在数据量较大时使用.

03

插入排序

    插入排序的原理和打牌时摸牌差不多,我们在打牌时按照顺序排好牌之后摸到一张新的,总是会将新的牌按照大小顺序插入到手牌当中,而插入排序则是假设前n个数是有序排列的,将n+1个数字插入到前n个数当中.

    如果说冒泡排序是前后两两对比以此循环的话,插入排序可以视为冒泡排序的变种.冒泡排序比较次数最多的是第一次,而插入排序则是最后一次,仔细想一下原理的话,类似于冒泡排序的排序方式倒过来.插入排序代码如下

/**
* 插入排序
*/
public static void InsertSort(Double[] arr) {
double temp;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j > 0; j–) {
if (arr[j] < arr[j - 1]) {
temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
} else {
break;
}
}
}
}
由以上代码可以看出来,插入排序的实现方式与冒泡排序极为类似,那么既然代码实现是差不多的,优缺点自然也是差不多的,插入排序效率依旧一言难尽.

作者运行插入排序排了30W条数据,输出的时间为:739390.大概为739秒左右,也不建议在大数据量时使用.

04

快速排序

    快排的原理就相对上面三种排序麻烦一些,先需要找到一个数字,然后以这个数字作为标杆,大于他的放在一边,小于他的放在另一边,然后继续对这两边进行相同操作(递归调用),最终实现将数组完全有序化.

    快速排序代码如下:

/**
* 快速排序
*/
public static void QuickSort(Double[] arr, int l, int r) {
if (l >= r)
return;
int i = l;
int j = r;
double key = arr[l];//选择第一个数为key
while (i < j) {
while (i < j && arr[j] >= key)//从右向左找第一个小于key的值
j–;
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && arr[i] < key)//从左向右找第一个大于key的值
i++;
if (i < j) {
arr[j] = arr[i];
j–;
}
}
arr[i] = key;
QuickSort(arr, l, i - 1);
QuickSort(arr, i + 1, r);
}
快速排序的原理既然与上面的三种原理截然不同,自然优缺点差距也很大,快速排序的性能非常的好,对于完全无序的数组所需耗时非常短,但是它性能并不稳定.

    作者运行快速排序对30W条数据进行排序的结果为:48.仅仅需要48毫秒,但是在对有序数组进行排序时,直接报了StackOverflowError,因为递归次数太多导致线程栈空间耗尽,因此可以看出来快速排序性能并不稳定,对于有序数组或基本有序数组并不能很好的达到要求,但这仍不能掩盖快速排序是排列无序数组最快的几种方式.

05

归并排序

    归并算法的原理有些类似快速排序,试想以下如何将两个有序数组合并成为一个有效数组呢,将两个数组的数据进行比较,谁的小取谁的,然后继续比较直至两个数组的数据被取光,那么我们要排序的值是无序的怎么办,只要继续递归下去,直到每个数组都只有一个数字,数组自然有序,这就是归并排序的来源,递归合并.

public static void MergeArray(Double a[],int first,int middle,int end,Double temp[]){
int i = first;
int m = middle;
int j = middle+1;
int n = end;
int k = 0;
while(i<=m && j<=n){
if(a[i] <= a[j]){
temp[k] = a[i];
k++;
i++;
}else{
temp[k] = a[j];
k++;
j++;
}
}
while(i<=m){
temp[k] = a[i];
k++;
i++;
}
while(j<=n){
temp[k] = a[j];
k++;
j++;
}
for(int ii=0;ii<k;ii++){
a[first + ii] = temp[ii];
}
}
归并算法的计算方式注定了它具有良好的性能及稳定性,在面对无序的30W数据数组时它只用了12毫秒,而面对有序数组时更是只用了5毫秒.对于大数据量下的排序,归并算法绝对是一个好选择.

05

总结

   在日常使用或者面试中,掌握上面五种基本算法其实差不多够用了,八种基本算法中的希尔排序效率不够高,基数排序无法针对小数排序,他们俩再加上堆排序的共同确定就是不够稳定.

    开发中如果涉及到数据量很大的情况下推荐使用归并排序或者从取数据时就将顺序定下来,如果并不涉及到数据量特别大的情况下,一般Arrays.sort也足够了,这个util自带的方法排序30W无序数据仅需要90毫秒左右.Arrays.sort中自带根据数组不同情况进行不同方法的排序,稳定而且性能不错.(ps:如果有人告诉你sort方法用的时冒泡不要信他,让他回去自己看源码)

    下面附带一个很有意思的算法,猴子算法,有兴趣的可以自己研究下,还是蛮有意思的:

public class MonkeySort {
public static void main(String[] args) {
List arr = new ArrayList();// 建一个无序的数组
arr.add(2);
arr.add(5);
arr.add(3);
arr.add(8);
arr.add(324);
arr.add(22);
arr.add(223);
arr.add(636);
arr.add(44);
Long sum = 0L;
Date sdate = new Date();
SortThread sortThread = new SortThread(arr, sdate, sum);
sortThread.run();
}
}
// 排序线程
class SortThread extends Thread {
List arr = null;
Thread t = null;
Date startDate = null;
Long sum = 0L;

public SortThread(List<Integer> arr, Date startDate, Long sum) {
    this.arr = arr;
    this.startDate = startDate;
    this.sum = sum;
}
public void run() {
    Collections.shuffle(arr);// 随机打乱
    System.out.println(arr);
    for (int i = 0; i < arr.size() - 1; i++) {
        if (arr.get(i) < arr.get(i + 1)) {
            continue;
        } else {
            sum++;
            new SortThread(arr, startDate, sum).start();
            return;// 结束当前线程
        }

    }
    System.err.println("花了" + (new Date().getTime() - startDate.getTime()) + "ms时间,排序数组元素个数:" + arr.size() + ",共进行了"
            + sum + "次");

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值