从冒泡排序到快速排序, 以及他们的优化方式

冒泡排序

在学习快速排序之前我们来看一下什么冒泡排序

import java.util.Arrays;

public class 冒泡排序 {
    public static void main(String[] args) {

        int[] test = new int[]{9, 1, 5, 8, 7, 4, 6, 2};
        //用于交换的临时值
        int temp = 0;
        for (int i = 0; i < test.length; i++) {
            for (int j = test.length - 1; j > i; j--) {
            //如果前一位数比后一位大则交换两个数
                if (test[j - 1] > test[j]) {
                    temp = test[j - 1];
                    test[j - 1] = test[j];
                    test[j] = temp;
                }
            }
        }
        //记得Java的话直接test.toString()可是没法输出数组的...
        System.out.println(Arrays.toString(test));

    }
}

冒牌排序的核心思路是两两交换, 它是一种时间换空间的排序算法, (因为它不耗费什么额外空间), 最坏情况是把逆序的数列变成顺序, 所以最差的时间复杂度是O(N^2), 最好的情况下是O(N)一遍过, 平均就是O(N^2)

优化方式一

比如我们现在要排序arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 9}按照我们原本的方式, 第一躺排序后10和9的交换已经有序了, 即1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
接下来的八躺都是多余的什么也没做, 所以我们可以在交换的地方加一个flag, 如果那一趟没有交换元素, 说明这组数据已经有序, 就不用再继续下去

优化方式二

优化方式一是默认后半部分无序, 前半部分有序, 这时候我们就可以反过来进行思考, 默认后半部分有序, 前半部分无序, 即像arr[] = {1, 2, 5, 7, 4, 3, 6, 8, 9 ,10}.
这里我们也可以加一个flag来记录最后一次交换的位置, 如果后边没有进行交换那必然是有序的,所以我们进行下一次排序的时候从第一个比较到上次记录的位置就可以

优化方式三

我们默认是一次排序确定一个值, 但是我们可以一次排序确定两个值!
正向扫描确认最大值到最后, 反向扫描找到最小值交换到最前面.

快速排序

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

就是你选择一个点, 然后左端有端两个指针, 把比那个点小的放在左边, 大的放右边, 递归处理.

最差情况显而易见, 划分之后一边是一个, 另一边是n-1个, 这种极端情况的时间复杂度就是O(N^2), 最好的情况就是每次都能均匀的划分序列(中轴两边的数相等), 时间复杂度就是O(N*log2N)

正因为如此, 所以它不稳定, 而冒泡排序是稳定的O(N^2)

现在让我们来尝试实现一下快速排序

package baguwen;

import java.util.Arrays;

public class quickSort {


    public static void main(String[] args) {
        int a[] = {3, 5, 9, 4, 11, 6, 7, 25, 33, 54, 99};
        quickSort(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
    }

    public static void quickSort(int[] a, int left, int right) {
        int pivot = a[(left + right) / 2];
        //pivot作为中轴 比它小的放在左边 比它大的放在右边
        int l = left;
        int r = right;
        while (l < r) {
            //开始比较! 只要左指针的位置不超过右指针的位置就不能停下来
            while (a[l] < pivot) {
                //在左侧找到一个比中轴值大的为止
                l = l + 1;
            }
            while (pivot < a[r]) {
                //在右侧找到一个比中轴值小的为止
                r = r - 1;
            }
            if (l <= r) {
                //交换他们俩!
                int temp = a[l];
                a[l] = a[r];
                a[r] = temp;
                l++;
                r--;
                //至于为什么要l++和r--,你想想如果不加的话是不是就陷入死循环了
            }
            //这样就进行完一个轮回啦, 马上就要开始下一个轮回了冲冲冲
        }
        if (left < r) {
            quickSort(a, left, r);
        }
        if (l < right) {
            quickSort(a, l, right);
        }
    }

}

优化

面向面试学习
我tm直接一步冲到三数取中选择中轴+插排+聚集相等元素

三数取中选择中轴

比如我们需要排序序列为: 9 1 4 0 6 3 5 2 7 5
左边为9 右边为5 中间为 6
我们取这三个数排序后, 中间那个数作为中轴(替代随机选取或者直接中间选取

当待排序序列长度分割到一定大小后, 使用插入排序

对于很小和部分有序的数组,快排不如插排好
所以当待排序序列长度为10的时候, 我们选择插入排序

聚集相同的元素

在一次分割结束后, 可以把与Key相等的元素聚在一起, 继续下次分割时, 不用再对与Key相等的元素进行分割
比如:
待排序序列arr[] = {1, 4, 6, 7, 6, 6, 7, 6, 8, 6}
先进行三数取中选取中轴1 6 6 选 6
替换后待分割序列为: 6 4 6 7 1 6 7 6 8 6
如果不对Key元素先进性处理再分割的话就为
1 4 6 6 7 6 7 6 8 6
再进行分割就是
1 4 6 以及7 6 7 6 8 6
如果对Key元素先进行处理再分割的话就为 1 4 6 6 6 6 6 7 8 7
再进行分割就是
1 4 和7 8 7
如果把Key相等的元素聚在一起, 就能减少迭代次数, 效率会提高不少

彩蛋

在这里插入图片描述
那么想要把简历按时间排序的话用什么排序算法最好呢?
(草,人肉计算的话还是冒泡吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值