zoukankan      html  css  js  c++  java
  • 快速排序

    快速排序进阶之三路快排——学习笔记

    原理

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

    使用了分冶法。

    分冶法是把一个规模为N的问题分成两个或多个较小的与原问题类型相同的子问题,通过对子问题的求解,并把子问题的解合并起来从而构成整个问题的解,即对问题各个击破,分而治之。如果子问题的的规模仍然相当大,仍不足以很容易的求得它的解,这时可以对此子问题重复的应用分冶策略。

    复杂度

    时间复杂度

    最坏情况:最坏情况发生在每次划分过程产生的两个区间分别包含n-1个元素和1个元素的时候。O(n2),退化成冒泡排序,即每次都排好一个元素的顺序。

    最好得情况:每次划分过程产生的区间大小都为n/2,则快速排序法运行就快得多了。O(nlogn)

    平均情况:O(nlogn)

    空间复杂度

    最优的情况下空间复杂度为:O(logn)  :每一次都平分数组的情况。

    最差的情况下空间复杂度为:O( n ) :退化为冒泡排序的情况。

    代码

    老版本(没有减小重复元素的影响,可以看作不完全的三路快排)

    public static void main(String[] args) {
      int[] n = { 2, 1, 7, 4, 8, 5 };
      quickSort(n, 0, n.length - 1);
      for (int i : n) {
        System.out.print(i + " ");
      }
    }
    
    public static void quickSort(int[] list, int left, int right) {
      if (left < right) {
        int middle = getMiddle(list, left, right); // 将list数组进行一分为二
        quickSort(list, left, middle - 1); // 对低字表进行递归排序
        quickSort(list, middle + 1, right); // 对高字表进行递归排序
      }
    }
    
    public static int getMiddle(int[] list, int left, int right) {
      int tmp = list[left]; // 数组的第一个作为中轴
      while (left < right) {
        while (left < right && list[right] >= tmp) {
          right--;
        }
        list[left] = list[right]; // 比中轴小的记录移到低端
        while (left < right && list[left] <= tmp) {
          left++;
        }
        list[right] = list[left]; // 比中轴大的记录移到高端
      }
      list[left] = tmp; // 中轴记录到尾
      return left; // 返回中轴的位置
    
    }

    二路快排(简单,最常用)

    public static void quickSort2(int[] n, int left, int right) {
            //[left,slow) 是小于key的数据集; (slow,right]是大于key的数据集
            if (n == null || n.length < 1 || left >= right) {
                return;
            }
            //int[] n = { 7, 1, 2, 8, 7, 2, 12, 7 };
            int key = n[left];//比较的基础值(可优化:见优化点)
            int slow = left;//基础值的下标(最后基础值也会放在这个下标点上)
            int fast = left + 1;//遍历数组的指针,从基础值的下一个元素开始
            while (fast <= right) {
                if (n[fast] < key) {
                    //大于等于key不动,小于key跟 n[slow + 1]交换
                    swap(n, ++slow, fast);
                }
                fast++;
            }
            swap(n, left, slow);//基础值还在left位置,交换一下,最后放在最右边的小于基础值的下标上
            //System.out.println(JSON.toJSONString(n) + " " + slow);
            quickSort2(n, left, slow - 1);
            quickSort2(n, slow + 1, right);
        }

       public static void swap(int[] n, int left, int right) {
           if (n[left] == n[right]) {
               return;
           }
           n[left] = n[left] ^ n[right];
           n[right] = n[left] ^ n[right];
           n[left] = n[left] ^ n[right];
       }

    三路快排(有效避免重复元素的反复排序)

    public static void quickSort3(int[] n, int left, int right) {
            //[left,slow)是小于key的数据集; (largeIndex,right]是大于key的数据集; [slow,largeIndex]是等于key的数据集
            if (n == null || n.length < 1 || left >= right) {
                return;
            }
            //int[] n = { 7, 1, 2, 8, 7, 2, 12, 7 };
            int key = n[left];//比较的基础值(可优化:见优化点)
            int slow = left;//基础值的下标(如果基础值有重复元素,此为最左下标)
            int fast = left + 1;//遍历数组的指针,从基础值的下一个元素开始
            int largeIndex = right;//由于大于基础值的会被交换到后面,所以最后此值为基础值下标(如果基础值有重复元素,此为最右下标)
            while (fast <= largeIndex) {
                if (n[fast] < key) {
                    swap(n, fast++, slow++);
                } else if (n[fast] > key) {
                    swap(n, fast, largeIndex--);
                } else {
                    fast++;
                }
            }
            //System.out.println(JSON.toJSONString(n) + " " + slow + " " + largeIndex);
            quickSort3(n, left, slow - 1);
            quickSort3(n, largeIndex + 1, right);
        }

    优化点

    中间值(解决基础值问题)

    与一般的快速排序方法不同,它并不是选择待排数组的第一个数作为中轴,而是选用待排数组最左边、最右边和最中间的三个元素的中间值作为中轴。这一改进对于原来的快速排序算法来说,主要有两点优势:
    (1) 首先,它使得最坏情况发生的几率减小了。
    (2) 其次,未改进的快速排序算法为了防止比较时数组越界,在最后要设置一个哨点。

    分区间隔小时可使用插入排序

    快速排序的优化可考虑当分区间隔小的的时候转而使用插入排序。

    五分钟学算法  双路、三路快排(对于有很多重复值的数据集很有效)

    三路快排(解决重复元素的反复排序问题)

    见代码

  • 相关阅读:
    架构基础-容量评估
    golang版本实现版本号比较-从易到解决bug
    数组模拟栈
    稀疏数组
    密码生成器
    01-gopsutil包使用
    02从零开始学习GO语言--标识符、关键字、变量和常量
    Go语言简介
    从零开始学习GO语言-搭建Go语言开发环境-快速开发入门第一个小程序
    ES6学习总结之 Module
  • 原文地址:https://www.cnblogs.com/fanguangdexiaoyuer/p/10539818.html
Copyright © 2011-2022 走看看