• 快速排序


    参考:快速排序(java实现)

    高效的排序算法

    假设我们现在对"6,1,2,7,9,3,4,5,10,8"这10个数进行排序.首先在这个序列中随便找一个基准数,就是一个用来参照的数.为了方便,就让第一个数6最为基准数.接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,类似下面这种排序:

    3 1 2 5 4 6 9 7 10 8

    在初始状态下,数字6在序列的第一位.我们的目标是将6挪到序列中间的某个位置,假设这个位置是k.现在就是需要找到这个k,并且以k为分界点,左边的数都小于等于6,右边的数都大于等于6.想一想,该怎样实现?

    快排思想

    实现上面的例子,就是需要使用快排的思想:分别从初始化序列"6 1 2 7 9 3 4 5 10 8"两端开始探索.先从找一个小于6的数,再从找一个大于6的数,然后交换它俩.这里可以使用两个变量i和j,分别指向序列最左边和最右边.我们为这两个变量起一个很好听的名字"哨兵i"和"哨兵j".刚开始的时候,让哨兵i指向序列最左边(即i=0),指向数字6.让哨兵j指向序列的最右边(即j=9),指向数字8

    • 首先哨兵j开始出动,哨兵j一步一步地向左挪动(即j--),知道找到一个小于6的数停下来;
    • 接下来哨兵i再一步一步向右挪动(即i++),知道找到一个大于6的数停下来.最后哨兵j停在了数字5上,哨兵i停在了数字7上.
    • 现在交换哨兵i和哨兵j所指向的元素值.
    • 第一次交换结束

    • 接下来哨兵j继续向左移动,重复上述的动作,发现了4
    • 哨兵i继续向右移动返现了9
    • 交换哨兵i和哨兵j的值
    • 第二次交换结束

    • 哨兵j继续向左移动,它发现了3,哨兵i继续向右移动,此时i和j就会相遇,那么就探测结束.我们将基准数6和3进行交换.



      到此第一轮探测结束,此时以6为分界点,6左边的都是小于等于6的数,6右边的都是大于等于6的数.回顾上面的过程,其实就是哨兵j的使命就是找到小于基准数的数,哨兵i的实名就是找到大于基准数的数,知道二者碰头为止.

      现在6已经就位了,此时原来的一个序列就以6为分界点分成了两个,左边序列"3,1,2,5,4",右边序列"9,7,10,8".接下来使用同样的方法,对这两个数组进行处理,按照上面的方法依次处理,直到所有的数都就位.

    注意
    快排之所以快,因为相比于冒泡,每次交换是跳跃式的.每次排序的时候设置一个基准点,将小于等于基准点的全部放到基准点的左边,将大于等于基准点的全部放到基准点的右边.这样在每次交换的时候就不会像冒泡排序一样每次只在相邻的数之间进行交换,交换的距离大了,因此总的比较次数和交换次数就少了,速度自然就提升了. 当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。

    代码实现

    public class QuickSort {
        public static void main(String[] args) {
            int[] arr = {10, 7, 2, 4, 7, 62, 3, 4, 2, 1, 8, 9, 19};
            System.out.println("排序前的数组为:");
            System.out.println(Arrays.toString(arr));
            quickSort(arr, 0, arr.length - 1);
            System.out.println("排序后的数组为:");
            System.out.println(Arrays.toString(arr));
        }
    
        private static void quickSort(int[] arr, int i, int i1) {
            if (i > i1) {
                return;
            }
            //定义左边指针和右边指针
            int left = i;
            int right = i1;
            //定义基准位
            int selectVal = arr[i];
            int temp;  // 交换辅助变量
    
            while (left < right) {
                // 右边比较,找到小于基准位的值
                while (arr[right] >= selectVal && left < right) {
                    right--;
                }
                //左边比较
                while (arr[left] <= selectVal && left < right) {
                    left++;
                }
                //跳出上面的while有两种情况,有可能找到了,有可能是已经排好序,遍历到条件结束
                if (left < right) {
                    //交换
                    temp = arr[left];
                    arr[left] = arr[right];
                    arr[right] = temp;
                }
            }
            //遍历到最后说明 right = left,找到了基准插入的值
            arr[i] = arr[left];
            arr[left] = selectVal;
    
            //左边递归
            quickSort(arr,i,right-1);
    
            //右边递归
            quickSort(arr,left+1,i1);
    
        }
    }
    
    排序前的数组为:
    [10, 7, 2, 4, 7, 62, 3, 4, 2, 1, 8, 9, 19]
    排序后的数组为:
    [1, 2, 2, 3, 4, 4, 7, 7, 8, 9, 10, 19, 62]
    
  • 相关阅读:
    WebApp之Meta标签 (关闭自动识别数字为电话号码或邮箱之类)
    opcache运行时配置参数详解
    lighttpd
    微信的数据结构--我做粉丝系统仿照这个思路
    MySQL5.6之Index Condition Pushdown(ICP,索引条件下推)-Using index condition
    使用 XHProf 分析你的 PHP 程序
    HHVM,高性能的PHP执行引擎
    MAC 设置环境变量path的几种方法
    命令:ln 使用方法
    mac, xcode 6.1 安装command line tools 支持,autoconf,automake等
  • 原文地址:https://www.cnblogs.com/liuzhidao/p/13815543.html
Copyright © 2020-2023  润新知