【问题标题】:QuickSelect Algorithm UnderstandingQuickSelect算法理解
【发布时间】:2012-06-01 07:53:38
【问题描述】:

我一直在研究各种讨论快速排序和快速选择的教程和文章,但是我对它们的理解仍然不稳定。

鉴于这种代码结构,我需要能够掌握并解释 quickselect 的工作原理。

// return the kth smallest item
int quickSelect(int items[], int first, int last, int k) {
    int pivot = partition(items, first, last);
    if (k < pivot-first) {
        return quickSelect(items, first, pivot, k);
    } else if (k > pivot) {
        return quickSelect(items, pivot+1, last, k-pivot);
    } else {
        return items[k];
    }
}

在分解为伪代码方面我需要一点帮助,虽然我没有获得分区函数代码,但我想了解它提供的快速选择函数会做什么。

我知道快速排序的工作原理,只是不知道快速选择。我刚刚提供的代码是我们获得的关于如何格式化快速选择的示例。

编辑:更正后的代码是

int quickSelect(int items[], int first, int last, int k) 
{
    int pivot = partition(items, first, last);
    if (k < pivot-first+1) 
    { //boundary was wrong
        return quickSelect(items, first, pivot, k);
    } 
    else if (k > pivot-first+1) 
    {//boundary was wrong
        return quickSelect(items, pivot+1, last, k-pivot);
    }
    else 
    {
        return items[pivot];//index was wrong
    }
}

礼貌:@海涛

【问题讨论】:

  • 我的需求可能过于具体 - 对快速选择的一般理解最有帮助,我提供的代码就是一个示例。
  • 来自麻省理工学院的信息是关于排序的,而不是选择的。据我所知。
  • 为什么我们需要这些:pivot-first+1 和 k-pivot

标签: algorithm quickselect


【解决方案1】:

快速选择的重要部分是分区。所以让我先解释一下。

快速选择中的分区选择pivot(随机或第一个/最后一个元素)。然后它重新排列列表,使所有小于pivot 的元素都位于枢轴左侧,其他元素位于右侧。然后它返回pivot 元素的索引。

现在我们在这里找到第 k 个最小的元素。分区后的情况是:

  1. k == pivot。那么你已经找到了第 k 个最小的了。这是因为分区的工作方式。恰好有小于kth 元素的k - 1 元素。
  2. k &lt; pivot。那么第 k 个最小的在 pivot 的左侧。
  3. k &gt; pivot。然后第 k 个最小的在枢轴的右侧。要找到它,您实际上必须找到右边的最小数字k-pivot

【讨论】:

  • 我提供的代码示例是否针对 k 进行了这 3 项检查?
  • 我注意到我没有检查 k == pivot
  • @Andrew, k == pivot 在您的代码的最后一个 else 块中被捕获。
  • @Andrew,也不是说k &lt; pivot - first 是参考通过的first 索引进行比较。
【解决方案2】:

顺便说一句,您的代码有一些错误..

int quickSelect(int items[], int first, int last, int k) {
    int pivot = partition(items, first, last);
    if (k < pivot-first+1) { //boundary was wrong
        return quickSelect(items, first, pivot, k);
    } else if (k > pivot-first+1) {//boundary was wrong
        return quickSelect(items, pivot+1, last, k-pivot);
    } else {
        return items[pivot];//index was wrong
    }
}

【讨论】:

  • 你的也有错误。第二个 quickSelect 应该是 quickSelect(items, pivot+1, last, k- (pivot-first+1));
【解决方案3】:

分区非常简单:它重新排列元素,使小于选定枢轴的元素在数组中的索引低于枢轴,而大于枢轴的元素在数组中的索引更高。

我在previous answer 中谈到了其余的内容。

【讨论】:

    【解决方案4】:
    int quickSelect(int A[], int l, int h,int k)
    {
            int p = partition(A, l, h); 
            if(p==(k-1)) return A[p];
            else if(p>(k-1)) return quickSelect(A, l, p - 1,k);  
            else return quickSelect(A, p + 1, h,k);
    
    }
    

    //分区功能同QuickSort

    【讨论】:

      【解决方案5】:

      我正在阅读 CLRS 算法书来学习快速选择算法,我们可以简单地实现该算法。

      package selection;    
      import java.util.Random;
      
      /**
       * This class will calculate and print Nth ascending order element
       * from an unsorted array in expected time complexity O(N), where N is the
       * number of elements in the array.
       * 
       * The important part of this algorithm the randomizedPartition() method.
       * 
       * @author kmandal
       *
       */
      public class QuickSelect {
          public static void main(String[] args) {
              int[] A = { 7, 1, 2, 6, 0, 1, 96, -1, -100, 10000 };
              for (int i = 0; i < A.length; i++) {
                  System.out.println("The " + i + "th ascending order element is "
                          + quickSelect(A, 0, A.length - 1, i));
              }
          }
      
          /**
           * Similar to Quick sort algorithm partitioning approach works, but after
           * that instead of recursively work on both halves here will be recursing
           * into desired half. This step ensure to the expected running time to be
           * O(N).
           * 
           * @param A
           * @param p
           * @param r
           * @param i
           * @return
           */
          private static int quickSelect(int[] A, int p, int r, int i) {
              if (p == r) {
                  return A[p];
              }
              int partitionIndex = randomizedPartition(A, p, r);
              if (i == partitionIndex) {
                  return A[i];
              } else if (i < partitionIndex) {// element is present in left side of
                                              // partition
                  return quickSelect(A, p, partitionIndex - 1, i);
              } else {
                  return quickSelect(A, partitionIndex + 1, r, i);// element is
                                                                  // present in right
                  // side of partition
              }
          }
      
          /**
           * 
           * Similar to Quick sort algorithm this method is randomly select pivot
           * element index. Then it swap the random pivot element index with right
           * most element. This random selection step is expecting to make the
           * partitioning balanced. Then in-place rearranging the array to make all
           * elements in the left side of the pivot element are less than pivot
           * element and the right side elements are equals or grater than the pivot
           * element. Finally return partition index.
           * 
           * @param A
           * @param p
           * @param r
           * @return
           */
          private static int randomizedPartition(int[] A, int p, int r) {
              int partitionIndex = p;
              int random = p + new Random().nextInt(r - p + 1);// select
                                                                  // pseudo random
                                                                  // element
              swap(A, random, r);// swap with right most element
              int pivot = A[r];// select the pivot element
              for (int i = p; i < A.length - 1; i++) {
                  if (A[i] < pivot) {
                      swap(A, i, partitionIndex);
                      partitionIndex++;
                  }
              }
              swap(A, partitionIndex, r);
              return partitionIndex;
          }
      
          /**
           * Swapping 2 elements in an array.
           * 
           * @param A
           * @param i
           * @param j
           */
          private static void swap(int[] A, int i, int j) {
              if (i != j && A[i] != A[j]) {
                  int temp = A[i];
                  A[i] = A[j];
                  A[j] = temp;
              }
          }
      }
      
      
      Output:
      The 0th ascending order element is -100
      The 1th ascending order element is -1
      The 2th ascending order element is 0
      The 3th ascending order element is 1
      The 4th ascending order element is 1
      The 5th ascending order element is 2
      The 6th ascending order element is 6
      The 7th ascending order element is 7
      The 8th ascending order element is 96
      The 9th ascending order element is 10000
      

      【讨论】:

      • 我仍然认为您可以在代码中添加一些解释。
      • 我添加了有意义的描述。
      【解决方案6】:
      int partition(vector<int> &vec, int left, int right, int pivotIndex)
      {
              int pivot = vec[pivotIndex];
              int partitionIndex = left;
      
              swap(vec[pivotIndex],vec[right]);
              for(int i=left; i < right; i++) {
                      if(vec[i]<pivot) {
                              swap(vec[i],vec[partitionIndex]);
                              partitionIndex++;
                      }
              }
              swap(vec[partitionIndex], vec[right]);
      
              return partitionIndex;
      }
      
      int select(vector<int> &vec, int left, int right, int k)
      {
              int pivotIndex;
              if (right == left) {
                      return vec[left];
              }
              pivotIndex = left + rand() % (right-left);
      
              pivotIndex = partition(vec,left,right,pivotIndex);
              if (pivotIndex == k) {
                      return vec[k];
              } else if(k<pivotIndex) {
                      /*k is present on the left size of pivotIndex*/
                      return partition(vec,left,pivotIndex-1, k);
              } else {
                      /*k occurs on the right size of pivotIndex*/
                      return partition(vec, pivotIndex+1, right, k);
              }
              return 0;
      }
      

      【讨论】:

      • 这是不正确的,当pivotIndex == k 返回该索引的值,而在其他情况下调用partition 并返回分区的索引。不知道为什么这被投票赞成。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-02
      • 2011-10-11
      • 1970-01-01
      • 2013-02-06
      • 2022-01-06
      • 2016-11-22
      相关资源
      最近更新 更多