classSolution { public: intquickSelect(vector<int>& a, int l, int r, int index){ int q = randomPartition(a, l, r); if (q == index) { return a[q]; } else { return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index); } }
inlineintrandomPartition(vector<int>& a, int l, int r){ int i = rand() % (r - l + 1) + l; swap(a[i], a[r]); returnpartition(a, l, r); }
inlineintpartition(vector<int>& a, int l, int r){ int x = a[r], i = l - 1; for (int j = l; j < r; ++j) { if (a[j] <= x) { swap(a[++i], a[j]); } } swap(a[i + 1], a[r]); return i + 1; }
publicintquickSelect(int[] a, int l, int r, int index) { intq= randomPartition(a, l, r); if (q == index) { return a[q]; } else { return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index); } }
publicintrandomPartition(int[] a, int l, int r) { inti= random.nextInt(r - l + 1) + l; swap(a, i, r); return partition(a, l, r); }
publicintpartition(int[] a, int l, int r) { intx= a[r], i = l - 1; for (intj= l; j < r; ++j) { if (a[j] <= x) { swap(a, ++i, j); } } swap(a, i + 1, r); return i + 1; }
publicvoidswap(int[] a, int i, int j) { inttemp= a[i]; a[i] = a[j]; a[j] = temp; } }
inlineintpartition(int* a, int l, int r) { int x = a[r], i = l - 1; for (int j = l; j < r; ++j) { if (a[j] <= x) { int t = a[++i]; a[i] = a[j], a[j] = t; } } int t = a[i + 1]; a[i + 1] = a[r], a[r] = t; return i + 1; }
inlineintrandomPartition(int* a, int l, int r) { int i = rand() % (r - l + 1) + l; int t = a[i]; a[i] = a[r], a[r] = t; return partition(a, l, r); }
intquickSelect(int* a, int l, int r, int index) { int q = randomPartition(a, l, r); if (q == index) { return a[q]; } else { return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index); } }
intfindKthLargest(int* nums, int numsSize, int k) { srand(time(0)); return quickSelect(nums, 0, numsSize - 1, numsSize - k); }
funcrandomPartition(a []int, l, r int)int { i := rand.Int() % (r - l + 1) + l a[i], a[r] = a[r], a[i] return partition(a, l, r) }
funcpartition(a []int, l, r int)int { x := a[r] i := l - 1 for j := l; j < r; j++ { if a[j] <= x { i++ a[i], a[j] = a[j], a[i] } } a[i+1], a[r] = a[r], a[i+1] return i + 1 }
复杂度分析
时间复杂度:O(n),如上文所述,证明过程可以参考「《算法导论》9.2:期望为线性的选择算法」。
空间复杂度:O(\log n),递归使用栈空间的空间代价的期望为 O(\log n)。
方法二:基于堆排序的选择方法
思路和算法
我们也可以使用堆排序来解决这个问题——建立一个大根堆,做 k - 1 次删除操作后堆顶元素就是我们要找的答案。在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让更面试者自己实现一个堆。所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。
classSolution { public: voidmaxHeapify(vector<int>& a, int i, int heapSize){ int l = i * 2 + 1, r = i * 2 + 2, largest = i; if (l < heapSize && a[l] > a[largest]) { largest = l; } if (r < heapSize && a[r] > a[largest]) { largest = r; } if (largest != i) { swap(a[i], a[largest]); maxHeapify(a, largest, heapSize); } }
voidbuildMaxHeap(vector<int>& a, int heapSize){ for (int i = heapSize / 2; i >= 0; --i) { maxHeapify(a, i, heapSize); } }
intfindKthLargest(vector<int>& nums, int k){ int heapSize = nums.size(); buildMaxHeap(nums, heapSize); for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) { swap(nums[0], nums[i]); --heapSize; maxHeapify(nums, 0, heapSize); } return nums[0]; } };
classSolution { publicintfindKthLargest(int[] nums, int k) { intheapSize= nums.length; buildMaxHeap(nums, heapSize); for (inti= nums.length - 1; i >= nums.length - k + 1; --i) { swap(nums, 0, i); --heapSize; maxHeapify(nums, 0, heapSize); } return nums[0]; }
publicvoidbuildMaxHeap(int[] a, int heapSize) { for (inti= heapSize / 2; i >= 0; --i) { maxHeapify(a, i, heapSize); } }
publicvoidmaxHeapify(int[] a, int i, int heapSize) { intl= i * 2 + 1, r = i * 2 + 2, largest = i; if (l < heapSize && a[l] > a[largest]) { largest = l; } if (r < heapSize && a[r] > a[largest]) { largest = r; } if (largest != i) { swap(a, i, largest); maxHeapify(a, largest, heapSize); } }
publicvoidswap(int[] a, int i, int j) { inttemp= a[i]; a[i] = a[j]; a[j] = temp; } }
voidmaxHeapify(int* a, int i, int heapSize) { int l = i * 2 + 1, r = i * 2 + 2, largest = i; if (l < heapSize && a[l] > a[largest]) { largest = l; } if (r < heapSize && a[r] > a[largest]) { largest = r; } if (largest != i) { int t = a[i]; a[i] = a[largest], a[largest] = t; maxHeapify(a, largest, heapSize); } }
voidbuildMaxHeap(int* a, int heapSize) { for (int i = heapSize / 2; i >= 0; --i) { maxHeapify(a, i, heapSize); } }
intfindKthLargest(int* nums, int numsSize, int k) { int heapSize = numsSize; buildMaxHeap(nums, heapSize); for (int i = numsSize - 1; i >= numsSize - k + 1; --i) { int t = nums[0]; nums[0] = nums[i], nums[i] = t; --heapSize; maxHeapify(nums, 0, heapSize); } return nums[0]; }
funcfindKthLargest(nums []int, k int)int { heapSize := len(nums) buildMaxHeap(nums, heapSize) for i := len(nums) - 1; i >= len(nums) - k + 1; i-- { nums[0], nums[i] = nums[i], nums[0] heapSize-- maxHeapify(nums, 0, heapSize) } return nums[0] }
funcbuildMaxHeap(a []int, heapSize int) { for i := heapSize/2; i >= 0; i-- { maxHeapify(a, i, heapSize) } }
funcmaxHeapify(a []int, i, heapSize int) { l, r, largest := i * 2 + 1, i * 2 + 2, i if l < heapSize && a[l] > a[largest] { largest = l } if r < heapSize && a[r] > a[largest] { largest = r } if largest != i { a[i], a[largest] = a[largest], a[i] maxHeapify(a, largest, heapSize) } }
复杂度分析
时间复杂度:O(n \log n),建堆的时间代价是 O(n),删除的总代价是 O(k \log n),因为 k < n,故渐进时间复杂为 O(n + k \log n) = O(n \log n)。