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

    1、思路不对,编出的代码肯定不对,因此编码之前,一定要现在草稿纸上,根据思路写出伪码。

    2、快速排序的思路:选一个目标,把数组分成两块,左边一块都比目标小,右边一块都比目标大。对每一块,递归做同样的事情,递归的出口时,左起点大于等于右起点。

    3、递归的形参表为:数组和排序的区间,排序区间也就是左起点和右起点,QuickSort(int a[], int lhs,int rhs),

    递归出口:if(lhs>=rhs) return;

    4、方法1:思路是对排序的区间做一个拷贝,取第一个为目标,从第二个开始,遍历排序区间,比目标小,从前往后。以此放在原数组区间的左边,比目标大,从后向前,一次放在原数组区间的右边,最后中间有个空格,把目标放在空格里,并且很容易知道空格的位置,对空格的前后两块递归。

    5、方法一很简单直观,但是要多分配一块内存,存储区间的内容。有没有更好的办法呢?

      方法2:思路是取第一个为目标,挖一个坑,从第二个开始遍历,比目标小,依次往前移,比目标大,应该放在后面,跟最后一个交换(下一次和倒数第二个交换,后面交换的位置前移一位),继续和目标比较,比目标小,往前移,比目标大,把倒数第二个换上来,以此类推。最后把目标填到空格里。

    6、方法二有个问题,那就是从尾部换上来的元素,比目标大,还要把这个元素换回去,注意:这里换回去的位置,是尾部的前一个元素。有没有更好的办发呢?

      方法二在第一个位置挖了个坑,比目标小的往前移,比目标大的,和最后面的元素交换,如果比目标小了,前移,如果还是比目标大,和次后面的元素交换。这就导致了换上来,有可能还要换回去。更好的办法是:既然前面已经有一个坑了,我从后面找到小元素,放到前面的坑里,这样后面就有了一个坑,再从前面找到一个大元素,放到后面的坑里,这样前面就挖了一个坑,最后剩下的坑放目标。

      方法3:在前面挖个坑,从后面找到一个小元素,挖出来,放到前面的坑里,后面就有了一个坑,再从前面找一个大元素,挖出来,放到后面的坑了。循环继续的条件是:前后没有走到一起。注意,这个时候,不适合在使用for循环了,当然也可以使用for。使用while表达循序继续的条件:往后走的位置小于往前走的位置,在循环的内部,首先从后面找到小元素,挖坑,使用while找到小元素,添数挖坑,然后使用while从前面找到大元素,添数挖坑。也就是说,用到三个while,外部的while判断是否走到了一起。内部第一个while,从后面挖坑,内部第二个while从前面挖坑。

    7、方法3代码如下: 

     1 template <typename T>
     2 void QuickSort(T* arr,int left,int right)
     3 {
     4  if(left < right)
     5  {
     6   int mid = Adjust(arr,left,right);
     7   QuickSort(arr,left,mid-1);
     8   QuickSort(arr,mid+1,right);
     9  }
    10 }
    11 
    12 
    13 template <typename T>
    14 int Adjust(T* arr, int i,int j)
    15 {
    16  T target = arr[i];
    17  while(i<j)
    18  {
    19   while(i<j && arr[j]>target)
    20   {
    21    --j;
    22   }
    23   if(i<j)
    24   {
    25    arr[i++] = arr[j];
    26   }
    27 
    28   while(i<j && arr[i]<target)
    29   {
    30    ++i;
    31   }
    32   if(i<j)
    33   {
    34    arr[j--] = arr[i];
    35   }
    36  }
    37 
    38  arr[i] = target;
    39  return i;
    40 }

     8、特别注意:一般情况下,我们使用的范围是[left, rigth),也就是左闭合区间。但是这里的快速排序,使用的区间是[left, right],也就是左右都闭合区间。当然,也可以使用左闭合区间,只不过稍微复杂一点,如下: 

     1 template <typename T>
     2 void QuickSort(T* arr,int left,int right)
     3 {
     4  // 只有一个元素,不需要排序。因此对于左闭合区间,比较left+1 与 right。
     5  // 注意:千万不能写成:++left<right。
     6  if(left+1 < right) 
     7  {
     8   int mid = Adjust(arr,left,right);
     9   QuickSort(arr,left,mid); //递归调用左闭合区间
    10   QuickSort(arr,mid+1,right);
    11  }
    12 }
    13 
    14 
    15 template <typename T>
    16 int Adjust(T* arr, int i,int j)
    17 {
    18  T target = arr[i];
    19  --j; // 首先移到有效元素
    20  while(i<j)
    21  {
    22   while(i<j && arr[j]>target)
    23   {
    24    --j;
    25   }
    26   if(i<j)
    27   {
    28    arr[i++] = arr[j];
    29   }
    30 
    31   while(i<j && arr[i]<target)
    32   {
    33    ++i;
    34   }
    35   if(i<j)
    36   {
    37    arr[j--] = arr[i];
    38   }
    39  }
    40 
    41  arr[i] = target;
    42  return i;
    43 }

     注意:if( left+1 < right ) 与 if( ++left < right )之间的区别,如果left为5,对于前者,判断之后left还是5,;对于后者,判断之后left就是6了。在上面的快速排序中,对于左闭合区间,判断是否只是一个元素,使用left+1。如果使用++left,判断之后,left累加1,if语句中的递归就会漏掉一个元素。

  • 相关阅读:
    脚本执行策略设置
    获取日期与时间戳小笔记
    Intellij IDEA部分简介
    Intellij IDEA脚本参数化、关联、检查点
    LR中的时间戳
    LeetCode 35.Search Insert Position
    LeetCode 34.Search for a Range
    LeetCode 33.Search in Rotated Sorted Array(M)
    LeetCode 81.Search in Rotated Sorted Array II(M)
    LeetCode 278.First Bad Version(E)(P)
  • 原文地址:https://www.cnblogs.com/nzbbody/p/3416438.html
Copyright © 2011-2022 走看看