1. 程式人生 > >快速選擇排序 Quick select 解決Top K 問題

快速選擇排序 Quick select 解決Top K 問題

1. 思想

   Quick select演算法通常用來在未排序的陣列中尋找第k小/第k大的元素。
   Quick select和Quick sort類似,核心是partition。 

   1. 什麼是partition?(如下圖,選44為pivot,把陣列分為2部分,左邊比44小,右邊比44大)

  

   從陣列中選一個數據作為pivot,根據每個陣列的元素與該pivot的大小將整個陣列分為兩部分:
   左半部分,都比pivot大,右半部分,都比pivot小 。

  2. 用分治思路實現排序

   pivotIndex 是pivot在陣列的下標

   pivotIndex大於k,說明array[pivotIndex]左邊的元素都大於k,只遞迴array[0, pivotIndex-1]第k大的元素即可;

   pivotIndex小於k,說明第k大的元素在array[pivotIndex]的右邊,只遞迴array[pivotIndex +1, n]第k-pivotIndex大的元素即可;

 

   邏輯如下:

function quickSelect(list, left, right, k)

   if left = right
      return list[left]

   Select a pivotIndex between left and right

   pivotIndex := partition(list, left, right, 
                                  pivotIndex)
   if k = pivotIndex
      return list[k]
   else if k < pivotIndex
      right := pivotIndex - 1
   else
      left := pivotIndex + 1

 

2.code in java

import java.util.Arrays;

public class QuickSelect {

    public static void main(String[] args) {
        int arr[] = {7, 10, 4, 3, 20, 15 };

        int pivotIndex = quickSelect(arr,4, 0, arr.length - 1 );
        System.out.println("pivotIndex="+pivotIndex);
    }

    
    private static int getPivotByPartition(int[] elements, int start, int end) {
        int pivot = start;
        int lessThan = start;

        for (int i = start; i <= end; i++) {
            int currentElement = elements[i];
            if (currentElement < elements[pivot]) {
                lessThan++;
                int tmp = elements[lessThan];
                elements[lessThan] = elements[i];
                elements[i] = tmp;
            }
        }
        int tmp = elements[lessThan];
        elements[lessThan] = elements[pivot];
        elements[pivot] = tmp;
        //System.out.println(" --- array = " +Arrays.toString(elements));
        return lessThan;
    }
    
    private static int quickSelect(int[] elements, int k, int start, int end) {

        int pivot = getPivotByPartition(elements, start, end);

        if (k == (pivot - start + 1)) {
            System.out.println("pivot value="+elements[pivot]);
            return pivot;
        } else if (k < (pivot - start + 1)) {
            return quickSelect(elements, k, start, pivot - 1);
        } else {
            return quickSelect(elements, k - (pivot - start + 1), pivot + 1, end);
        }
    }
}

3. 分析

與Quick sort不同的是,Quick select只考慮所尋找的目標所在的那一部分子陣列,而非像Quick sort一樣分別再對兩邊進行分  割。正是因為如此,Quick select將平均時間複雜度從O(nlogn)降到了O(n)

最壞時間複雜度: О(n²)

平均時間複雜度: О(n)

最壞空間複雜度: О(n) total, O(1) auxiliary

 

4. 參考

 

https://www.geeksforgeeks.org/quickselect-algorithm/

https://blog.csdn.net/wufaliang003/article/details/82940218