目录

       

前言:

一.算法思想:

 二.步骤:

三.算法优化:

四.代码实现:

五.算法评判:

六.小结:

       

前言:

        前两周因为机器人省赛以及后续和大家出去庆祝,学习java数据节后和算法的节奏被打断了。今天就用这一篇博客来重返学习状态,也借此给大家提醒:学习计划中不应该单单包含计划,还应该包含计划中断后怎么办,也就是容错。

        接下来我们正式开始快速排序:

一.算法思想:

        快速排序采用分治的思想,先找一个基准,比基准小的放到基准左边,比基准大的放基准右边。接下来去递归基准的左边,再去递归基准的右边,使得数组达到有序。

 二.步骤:

        原数组:int[ ] array = {2, 8, 3, 5, 9, 1, 7, 4, 6};

                              

        给定排序的范围begin 和 end 并选定最左侧的2作为基准,让 left 从 begin 开始,right 从 end 开始,先让 right 从右往左找比基准(2)小的数,right找到1,再让left从左往右找比2大的数,left找到8.

                                        

        让此时 left < right ,让二者指向的数字进行交换。

                                   

        让 right 继续从右往左找比2小的数,找到1,此时 right 和 left 相等。我们就让基准和left 与 right 相遇的下标所指向的数字进行交换。并把相遇的位置作为我们的 pivot。我么把这个过程叫做一次划分,(partition),经过一次划分。pivot就来到了排序后的正确的位置,接下来只需要递归pivot的左侧和右侧即可。

                                            

        接下来去递归 pivot 的左侧,begin 依旧是之前的 begin , end 变成了 pivot - 1 . 此时我们可以发现,begin 和 end 相遇了,说明需要排序的子数组只有一个元素,我们直接 return 即可。

                        

        接下来去递归 pivot 的右侧,注意,return 后的 pivot 还是在1下标,那么右侧的 begin 就是pivot + 1,end 就是 之前的 end。

                     

        继续一次划分,right 找比3小的,直接找到了left,此时 left 和 right 相等,我们就让left 和 right 相等的下标和基准(3)的下标进行交换,也就是让3和3自己交换。此时我们就会发现让right先动的好处:

        right从右往左找比基准小

        left从左往右找比基准大

        我们最后要把相遇点和基准进行交换,就要保证 相遇点 <= pivot 

        若左边先走,停下来的条件是比基准大,若此时右边追上来,相遇点的值比基准大。

        若右边先走,停下来的条件是比基准小,若此时左边追上来,相遇点的值比基准小。

        若右侧停下来的时候是一路走到了最左侧,我们就让 left 和自己交换,也没问题。

        总结一下就是 ,相遇点的值必须 ≤ pivot,才能和最左边的pivot安全交换。右边先走保证了这一点。

                            

        接下来的过程就是一次划分的过程,我们直接用图来表示: 


               

        

                     

            

       

        此时再去递归8的左右会发现已然有序,当begin = right ,也就是 子数组只有一个元素时,返回。我们就完成了快速排序。

三.算法优化:

        当待排序数组完全有序的时候,我们把left作为基准,会发现每次一趟划分的基准都只有"右子树",没有"左子树",就会出现下图的情况:

                           

        递归层数为n,每次递归都会开辟新的栈空间,就会造成内存的浪费。我们可以采用三数取中法来解决这个问题。

        三数取中法,顾名思义,就是找到left,right,和mid下标的中位数。把这个中位数放到开头,作为基准。这样一来,如果数组是排好序的,我们就可以保证基准的左右子树的节点数是一样的,若数组是无序的,那就和把left作为基准相差不大。

四.代码实现:

                

        

class Sort{
  public static void quickSort(int[] array,int left,int right){
    if(left >= right) return;
    int oriRight = right;
    int poivt = left;
    while(left < right){
      while(array[right] >= array[poivt] && left < right){
        right--;
      }
      while(array[left] <= array[poivt] && left < right){
        left++;
      }
      if(left < right){
        swap(array,left,right);
      }
    }
    swap(array,left,poivt);
    quickSort(array,poivt,left - 1);
    quickSort(array,left + 1,oriRight);
  }
  
  //交换
  public static void swap(int[] array,int i,int j){
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }
}

class Test{
  public static void main(String[] args){
    int[] array = {2,8,3,5,9,1,7,4,6};
    Sort.quickSort(array,0,array.length - 1);
  }
}

        运行结果:

快速排序:
排序前:[2, 8, 3, 5, 9, 1, 7, 4, 6]
排序后:[1, 2, 3, 4, 5, 6, 7, 8, 9]

五.算法评判:

        时间复杂度:O(nlog n)

        空间复杂度:O(log n)

        稳定性:不稳定

六.小结:

       

        自学编程的过程中,卡住并不可怕。可怕的是卡住之后把代码扔一边去逃避。

这次的经历让我明白,看懂了”和“会写了”之间隔着一条大鸿沟。当你代码卡住时,试着用最朴实的语言把它的执行流程讲给别人(或者写在博客里)听。当你能清晰解释的那一刻,Bug 往往自己就浮出水面了。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐