排序(令我烧脑的东西,跪着也要理解)

Posted 可乐好哇!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序(令我烧脑的东西,跪着也要理解)相关的知识,希望对你有一定的参考价值。

排序(Sort)

概念

   排序,就是使一串记录按照其中某个或某些关键字的大小,递增或递减的排列起来的操作。通常指的是排升序。

  • 稳定性:两个相等的数据,如果经过排序后,排序算法能保证它相对位置不发生变化,这样称该算法具备稳定性的排序算法

基于排序的总览(七种)

图示如下:

插入排序

  • 直接插入排序:每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入

  • 插入排序的优化:在有序区间选择数据应该插入的位置时,因为区间的有序性,可以利用折半查找的思想

    // 1.插入排序
    public static void insertSort(int[] array) 
    
        for (int i = 1; i < array.length; i++) 
    		
            int tmp = array[i]; //无序区间的第一个数
    
            int j = i - 1;
            for (; j >= 0; j--) 
                if (array[j] > tmp) 
                    array[j + 1] = array[j];
                 else 
                    break;
                
            
            array[j + 1] = tmp;
        
    
    // 折半建议了解
    
  • 时间复杂度O(n²)

  • 空间复杂度O(1)

  • 稳定性:稳定

  • 代码实现如下:

希尔排序

  • 希尔排序法:称缩小增量法,思想是先选定一个整数,把待排序文件中所有记录分成各组,所有距离的记录分在同一组内,并对每一组内的记录进行排序,然后取。重复分组和排序的工作。当到达1时,所有记录就已经排好序。
  • 希尔排序是对直接插入排序的优化。
  • 当gap > 1时都是预排序,目的是让数组更接近于有序。
  • 代码实现如下:
    // 2.希尔排序
   public static void shell(int[] array, int gap) 
       for (int i = gap; i < array.length; i++) 

           int tmp = array[i];
           int j = i - gap;

           for (; j >= 0; j = j - gap) 
               if (array[j] > tmp) 
                   array[j + gap] = array[j];
                else 
                   break;
               
           
           array[j + gap] = tmp;
       
   

   public static void shellSort(int[] array) 
       int[] drr = 5, 3, 1;
       for (int i = 0; i < drr.length; i++) 
           shell(array, drr[i]);
       
   
  • 时间复杂度O(n^1.3 ~ n^1.5)
  • 空间复杂度O(1)
  • 稳定性:不稳定

选择排序

  • 每一次从无序区间选出最大的一个元素,存放在无序区间的最后,知道全部待排序的数据元素拍完。
  • 代码实现如下:
	// 3.选择排序
   public static void selectSort(int[] array) 

       for (int i = 1; i < array.length; i++) 

           for (int j = i + 1; j < array.length; j++) 
               if (array[i] > array[j]) 
                   int tmp = array[i];
                   array[i] = array[j];
                   array[j] = tmp;
               
           
       
   
  • 时间复杂度O(n²)
  • 空间复杂度O(1)
  • 稳定性:不稳定

堆排序

  • 基本原理是选择排序,通过堆来选择无序区间的最大的数
    注意:排升序要建大堆;排降序要建小堆。
  • 代码实现如下:
    // 4.堆排序
   // 创建大堆
   public static void createHeap(int[] array) 
       for (int i = (array.length - 1) / 2; i >= 0; i--) 
           adjustDown(array, array.length, i);
       
   

   // 向上调整
   public static void adjustDown(int[] array, int len, int parent) 

       int leftChild = 2 * parent + 1;

       while (leftChild < len) 

           int maxChild = leftChild;
           int rightChild = 2 * parent + 2;

           if (rightChild < len) 
               if (array[rightChild] > array[leftChild]) 
                   maxChild = rightChild;
               
           

           if (array[parent] >= array[maxChild]) 
               break;
           

           int tmp = array[parent];
           array[parent] = array[maxChild];
           array[maxChild] = tmp;

           parent = maxChild;
           leftChild = 2 * parent + 1;
       
   

   // 交换数值
   public static void swap(int[] array, int i, int j) 
       int tmp = array[i];
       array[i] = array[j];
       array[j] = tmp;
   

   public static void heapSort(int[] array) 

       createHeap(array);

       for (int i = 0; i < array.length - 1; i++) 

           swap(array, 0, array.length - i - 1);

           adjustDown(array, array.length - i - 1, 0);

       

   
  • 时间复杂度O(nlog(2^n))
  • 空间复杂度O(1)
  • 稳定性:不稳定

冒泡排序

  • 通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
  • 代码实现如下:
    // 5.冒泡排序
   public static void bubbleSort1(int[] array) 
       for (int i = 0; i < array.length - 1; i++) 
           for (int j = 0; j < array.length - 1 - i; j++) 
               if (array[j] > array[j + 1]) 
                   int tmp = array[j];
                   array[j] = array[j + 1];
                   array[j + 1] = tmp;
               
           
       
   

   // 优化后的冒泡排序
    public static void bubbleSort2(int[] array) 
       for (int i = 0; i < array.length - 1; i++) 
           boolean flag = false;
           for (int j = 0; j < array.length - 1 - i; j++) 
               if (array[j] > array[j + 1]) 
                   int tmp = array[j];
                   array[j] = array[j + 1];
                   array[j + 1] = tmp;
                   flag = true;
               
           
           if (flag = false) 
               break;
           
       
   
  • 时间复杂度O(n²)
  • 空间复杂度O(1)
  • 稳定性:稳定

快速排序(重点掌握)

  • 从待排序区间选择一个数作为基准值
  • Partition:遍历整个待排序区间,将比基准值小的当道基准值的左边,反之放在右边
  • 采用分治思想对左右两个小区间按照同样的方式处理,直到小区间的长度等于1,这时代表已经有序了,或者小区间的长度等于0,代表没有数据了
  • 代码实现如下:
    // 6.快排
   public static int partion(int[] array, int left, int right) 

       int tmp = array[left];

       while (left < right) 
           while (left < right && array[right] >= tmp) 
               right--;
           

           if (left >= right) 
               array[left] = tmp;
               break;
            else 
               array[left] = array[right];
           

           while (left < right && array[left] <= tmp) 
               left++;
           

           if (left >= right) 
               array[right] = tmp;
               break;
            else 
               array[right] = array[left];
           
       
       return left;
   

   public static void quick(int[] array, int low, int hight) 

       if (low >= hight) 
           return;
       

       int par = partion(array, low, hight);
       quick(array, low, par - 1);
       quick(array, par + 1, hight);
   

   public static void quickSort(int[] array) 
       quick(array, 0, array.length - 1);
   


   // 快排优化1
   public static void quick2(int[] array, int low, int hight) 

       if (low >= hight) 
           return;
       

       // 快排优化2
       if (hight - low + 1 < 100) 
           insertSort2(array, low, hight);
           return;
       

       // 快排优化1
       int mid = (low + hight) >>> 1;

       medianOfThree(array, low, mid, hight);

       int par = partion(array, low, hight);
       quick(array, low, par - 1);
       quick(array, par + 1, hight);
   

   public static void insertSort2(int[] array, int start, int end) 

       for (int i = start; i <= end; i++) 

           int tmp = array[i];

           int j = i - 1;
           for (; j >= start; j--) 
               if (array[j] > tmp) 
                   array[j + 1] = array[j];
                else 
                   break;
               
           

           array[j + 1] = tmp;
       
   
  • 时间复杂度O(nlog(2^n))
  • 空间复杂度O(log(2^n))
  • 稳定性:稳定

优化总结

  • 选择基准值很重要,通常用几个数取中法
    1. 选择左边或者右边
    2. 随机选取
    3. 几个数取中法

  • partition过程中把和基准值相等的数也选择出来

  • 待排序区间小于一个阈值时,使用直接插入排序

归并排序(重点)

  • 建立在归并操作上的一种有效的排序算法,该算法也采用分治法。将已有的子序列合并,得到完全有序的序列,及先使每个子序列有序,再使子序列段有序,将两个有序表合并成一个有序表,称为二路归并。
  • 代码实现如下:
	// 7.归并排序
   public static void merge(int[] array, int left, int mid, int right) 

       int s1 = left;
       int e1 = mid;
       int s2 = mid + 1;
       int e2 = right;

       int[] tmpArray = new int[right - left + 1];
       int k = 0;

       while (s1 <= e1 && s2 <= e2) 
           if (array[s1] <= array[s2]) 
               tmpArray[k++] = array[s1++];
            else 
               tmpArray[k++] = array[s2++];
           
       

       while (s1 <= e1) 
           tmpArray[k++] = array[s1++];
       

       while (s2 <= e2) 
           tmpArray[k++] = array[s2++];
       

       for (int i = 0; i < tmpArray.length; i++) 
           array[i + left] = tmpArray[i];
       


   

   public static void mergeSortRec(int[] array, int left, int right) 
       if (left >= right) 
           return;
       

       int mid = (left + right) / 2;

       mergeSortRec(array, left, mid);
       mergeSortRec(array, mid + 1, right);

       merge(array, left, mid, right);

   

   public static void mergeSort(int[] array) 
       mergeSortRec(array, 0, array.length - 1);
   
  • 时间复杂度O(nlog(2^n))
  • 空间复杂度O(n)
  • 稳定性:稳定

海量数据的排序问题

  • 外部排序:排序过程需要在磁盘等外部存储进行的排序,因为内存中无法把所有数据全部放下,归并排序是最常用的外部排序。
    1. 先把文件切分为200份,每个512M
    2. 分别对512M排序
    3. 进行200路归并,同时对200份有序文件做归并过程,最终结果就有序了

总结

  1. 明确七大基于比较的排序算法原理,尤其是快速排序归并排序
  2. 自主完成排序算法的实现
  3. 了解海量数据排序问题的基本思路

以上是关于排序(令我烧脑的东西,跪着也要理解)的主要内容,如果未能解决你的问题,请参考以下文章

二零二零,自己选的路,跪着也要走完

ybt1195 判断整除(自己立的flag,跪着也要打完)

重开吐槽有感及OI书籍题库推荐 -自己选的路 跪着也要走完

蛋疼的iframe,烧脑的跨页面

前端异步代码烧脑的原因

tableau一道烧脑的表计算面试题