快速排序及其Partition函數(shù)的應(yīng)用

平均時間復(fù)雜度: O(NLogN)
最好情況時間復(fù)雜度: O(NLogN)
最差情況時間復(fù)雜度: O(N^2)
所需要額外空間: O(NLogN)
穩(wěn)定性:不穩(wěn)定

快速排序采用了分治策略,其基本思想是:

通過一趟排序,將待排記錄分割為獨立的兩部分,其中一部分記錄的關(guān)鍵字均比另一部分記錄的關(guān)鍵字小。
不斷重復(fù)這個步驟,直到每個部分都只有一個記錄為止。

其核心是一個Partition函數(shù),這個函數(shù)可以選取一個關(guān)鍵字(稱之為Pivot),然后把它放到一個合適的位置,使在它左邊的值都比它小,在它右邊的值都比它大。

具體實現(xiàn)(In JAVA)

package com.yenghye.sort;

public class Sort {

    public static void QuickSort(int[] arr, int low, int high) {
        int pivot;
        if (low < high) {
            pivot = Partition(arr, low, high);
            QuickSort(arr, low, pivot - 1);
            QuickSort(arr, pivot + 1, high);
        }
    }

    private static int Partition(int[] arr, int low, int high) {
        int pivotkey = arr[low];

        while (low < high) {
            while (low < high && pivotkey <= arr[high])
                high--;
            arr[low] = arr[high];

            while (low < high && pivotkey >= arr[low])
                low++;
            arr[high] = arr[low];
        }
        arr[low] = pivotkey;

        return low;
    }
}

Partition函數(shù)除了可以應(yīng)用在快速排序算法當(dāng)中,還可以用來實現(xiàn)對數(shù)組中第k大的數(shù)字的查找。
比如題目

數(shù)組中有一個數(shù)字出現(xiàn)的次數(shù)超過數(shù)組長度的一半,請找出這個數(shù)字。例如輸入一個長度為9的數(shù)組{1,2,3,2,2,2,5,4,2}。由于數(shù)字2在數(shù)組中出現(xiàn)了5次,超過數(shù)組長度的一半,因此輸出2。如果不存在則輸出0。

容易想到,如果一個數(shù)組當(dāng)中某個元素的個數(shù)超過了數(shù)組長度的一半,那么如果對這個數(shù)組排序,這個數(shù)組正中間位置上的數(shù)一定是我們要找的那個數(shù)字。
那么接下來的事很簡單,我們只要用一個現(xiàn)成的函數(shù):Partitan,如果我們得到的pivot剛剛好在數(shù)組的正中間,那么我們就找到了這個數(shù),如果沒有得到,根據(jù)pivot和正中間的位置關(guān)系,縮小范圍繼續(xù)查找就可以了。
實現(xiàn)(In JAVA)

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length == 0)
            return 0;
        int middle = array.length >> 1;
        int start = 0;
        int end = array.length - 1;
        int pivot = Partition(array, start, end);
        
        while(pivot != middle)
        {
            if(pivot > middle)
            {
                end = pivot - 1;
                pivot = Partition(array, start, end);
            }
            else
            {
                start = pivot + 1;
                pivot = Partition(array, start, end);
            }
                
        }
        int result = array[middle];
        if(CheckMoreThanHalf(array, result)!=0)
            return 0;
        return result;
        
    }
    
    private int CheckMoreThanHalf(int[] array, int result)
    {
        int times = 0;
        for(int i = 0; i < array.length; i++)
        {
            if(result == array[i])
                times++;
        }
        
        if((times << 1) > array.length)
            return 0;
        return 1;
    }
    
    private int Partition(int[] a, int low, int high)
    {
        int pivotkey = a[low];
        while(low < high)
        {
            while(low < high && a[high] >= pivotkey)
                high--;
            a[low] = a[high];
            
            while(low < high && a[low] <= pivotkey)
                low++;
            a[high] = a[low];
        }
        a[low] = pivotkey;
        return low;
    }
}

***除了用Partition,這道題還有另一種解法,其解題核心是:數(shù)組中的一個數(shù)字出現(xiàn)的次數(shù)超過數(shù)組長度的一半,就意味著它出現(xiàn)的次數(shù)比其他所有的數(shù)字出現(xiàn)的次數(shù)加起來還多。那么我們只要從前往后遍歷整個數(shù)組,選中一個數(shù)字為備選,并記錄它的次數(shù),如果接下來出現(xiàn)的數(shù)字和備選相等,那么增加次數(shù),不等則減少次數(shù)。當(dāng)次數(shù)為0的時候,則說明遍歷到此為止,相等和不等的個數(shù)相互抵消了,則應(yīng)當(dāng)選擇一個新的備選,并將次數(shù)設(shè)置為1.
這個思路很清晰,代碼寫出來也很簡單,就不寫了。


接下來再舉個用Partition函數(shù)解決問題的例子。

輸入n個整數(shù),找出其中最小的K個數(shù)。例如輸入4,5,1,6,2,7,3,8這8個數(shù)字,則最小的4個數(shù)字是1,2,3,4,。

想到用Partition函數(shù)來解決問題很容易,一直找直到pivot為k-1就可以了。這樣從下標(biāo)0到下標(biāo)k-1的k個數(shù),就是最小的k個數(shù)了。
但是解決問題之前要知道,Partition會改變輸入數(shù)組內(nèi)的元素順序,以及,這種方法比較適合n的數(shù)量較小的情況。

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        if(input == null || input.length == 0 || input.length < k)
            return new ArrayList();
        
        int start = 0;
        int end = input.length-1;
        int pivot = Partition(input, start, end);
        
        while(pivot != k-1){
            if(pivot > k-1){
                end = pivot - 1;
                pivot = Partition(input, start, end);
            }
            else {
                start = pivot + 1;
                pivot = Partition(input, start, end);
            }
        }
        ArrayList<Integer> result = new ArrayList();
        for(int i = 0; i < k; i++)
            result.add(Integer.valueOf(input[i]));
        return result;
    }
    
    private int Partition(int[] a, int low, int high)
    {
        int pivotkey = a[low];
        while(low < high) {
            while(low < high && a[high] >= pivotkey)
                high--;
            a[low] = a[high];
            
            while(low < high && a[low] <= pivotkey)
                low++;
            a[high] = a[low];
        }
        a[low] = pivotkey;
        return low;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗。 張土汪:刷leetcod...
    土汪閱讀 12,890評論 0 33
  • 首先總結(jié)以下Java和C、C++中的一般控制臺輸入方式,方便以后的編程題: java鍵盤輸入 java讀文件(會自...
    androidjp閱讀 2,379評論 0 16
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,347評論 0 2
  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,297評論 0 52
  • 體驗入:今天第一次嘗試自己動手做粗糧面 包(用面包機),我草草看了說明書, 就急于動手操作,結(jié)果漏掉...
    小玉空間閱讀 195評論 0 0

友情鏈接更多精彩內(nèi)容