我們都知道,查找第k大數有一個很常用的方法,是基於快排的查找,思路跟快排基本一樣,代碼如下:
public int findKthLargest(int[] nums, int k) { return (findKthNum(nums,0,nums.length-1,k-1)); } private int findKthNum(int[] nums, int left, int right, int k){ int stan = nums[right]; int i = left, j = right; if (left == right && left == k) { return nums[left]; } else { while (i < j) { while (i < j && nums[i] >= stan) { i++; } while (i < j && nums[j] <= stan){ j--; } int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } } nums[right] = nums[i]; nums[i] = stan; if (k < i) { return findKthNum(nums, left, i - 1, k); } else if (k > i) { return findKthNum(nums, i + 1, right, k); } else { return nums[i]; } }
因為每次分割完只需要繼續操作一邊,所以時間復雜度是O(n)。
但光是這樣解釋讓人有點不清楚,不通過計算很難讓人明白為什么是O(n),比如,即便每次只操作一邊,那么遍歷的次數也是logn次,每次遍歷是O(n),很容易讓人誤解為該方法是O(nlogn)。
所以這里還是要回到時間復雜度專門的遞推公式上來。
我們都知道,快速排序的理想以及平均時間復雜度是O(nlogn),其遞推公式如下,跟歸並排序基本無異,只是多了一個在理想或者平均的限制,因為快排的最壞是O(n^2)
平均情況下:T(n)=2*T(n/2)+n; 第一次划分 =2*(2*T(n/4)+n/2)+n; 第二次划分 (=2^2*T(n/4)+2*n) =2*(2*(2*T(n/8)+n/4)+n/2)+n; 第三次划分(=2*3*T(n/8)+3*n) =..................... =2^m+m*n; 第m次划分 因為2^m=n,所以等價於 = n+m*n 所以m=logn,所以T(n)=n+n*logn;
這邊還是比較好理解的,那么基於快排的查找第k大數的時間復雜度如下:
平均情況下:T(n) = T(n/2) + n; 第一次划分 = T(n/4) + n/2 + n; 第二次划分 = T(n/8) + n/4 +n/2 +n; 第三次划分 =..................... = T(n/n) + 2 + 4 + ... + n; 第m次划分 是一個等比數列的求和公式,那么顯然T(n) = 2n
所以計算時間復雜度最好嚴謹按照遞推公式來,那么這里注意基於快排查找第k大數,也是平均時間復雜度為O(n)。
《算法導論》介紹到一個最壞情況下也是O(n)的方法,這里不做詳細介紹,參考http://blog.chinaunix.net/uid-26456800-id-3407406.html