整数数组前K大的问题

这类问题是最高频K项的基础问题。问题描述如下:
在一个整数数组中,找最大的k个整数

离线算法

这离线处理中,可以使用Quick Select算法。在O(N)的时间内找到数组的第K大的数,然后扫描一次整个数组就可以得到前K大的数。该算法基于快排思想。

Quick Select

Quick Select
算法基本思想:

  1. 利用快排的分治思想,求得待搜索数组的主元pivot,以主元为界分成左右两个区间
  2. 通过比较主元的位置,判断第K个大小的数在主元左区间还是主元右区间?还是就是主元?(还要注意边界条件的判断,有可能在边界)
  3. 进入子区间递归调用
import java.util.Random;

class Solution {
    /*
     * @param nums an integer array
     * @param k an integer
     * @return the top k largest numbers in array
     */
    public int[] topk(int[] nums, int k) {
        // Write your code here
        quickSort(nums, 0, nums.length - 1, k);

        int[] topk = new int[k];
        for (int i = 0; i < k; ++i)
            topk[i] = nums[i];

        return topk;
    }
    
    private void quickSort(int[] A, int start, int end, int k) {
        if (start >= k)
            return;

        if (start >= end) {
            return;
        }
        
        int left = start, right = end;
        // key point 1: pivot is the value, not the index
        //创建一个随机数生成器
        Random rand =new Random(end - start + 1);
        //nextInt(int n)方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n
        int index = rand.nextInt(end - start + 1) + start;
        int pivot = A[index];

        // key point 2: every time you compare left & right, it should be 
        // left <= right not left < right
        while (left <= right) {
            // key point 3: A[left] < pivot not A[left] <= pivot
            while (left <= right && A[left] > pivot) {
                left++;
            }
            // key point 3: A[right] > pivot not A[right] >= pivot
            while (left <= right && A[right] < pivot) {
                right--;
            }

            if (left <= right) {
                int temp = A[left];
                A[left] = A[right];
                A[right] = temp;
                
                left++;
                right--;
            }
        }
        
        quickSort(A, start, right, k);
        quickSort(A, left, end, k);
    }
}

在线算法

对于数据流的形式,没有机会从头遍历。解决办法是考虑堆。这个问题中我们不需要支持删除操作,数据只增不减,那么那些已经排到第K名之后的数据,是再也没有机会进入前k名的,因此保留这些数据的意义不大,我们可以删掉它们。这样一来,我们需要维护一个最小堆。当我们需要决定一个整数是否能加入TopK的集合的时候,决定性的因素是:当前整数是否比Top K中的最小的数要大。因此在TopK的k个整数中,我们应该维护一个最小堆,推举出最小的整数去和数据流中新出来的整数作比较,如果新来的数更大,就踢掉堆里最小的整数,把新数加进去,反之则扔掉新数。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        if (k <= 0 || nums.length == 0 || nums == null) {
            return 0;
        }
        PriorityQueue minHeap = new PriorityQueue();
        for (int i = 0; i < nums.length; i++) {
            minHeap.add(nums[i]);
            if (minHeap.size() > k) {
                minHeap.poll();
            }
        }
        return minHeap.poll();
    }
}

快速排序实现

# quicksort

class Solution:

    def quicksort(self, A, start, end):

        if A is None or len(A) == 0:
            return None

        if start >= end:
            return None

        left = start
        right = end
        # pivot is value not index
        pivot = start + (end - start) // 2

        # 确保是left <= right,防止stackoverflow
        while left <= right:
            while left <= right and A[left] < pivot:
                left += 1
            while left <= right and A[right] > pivot:
                right -= 1
            if left <= right:
                A[left], A[right] = A[right], A[left]
                left += 1
                right -= 1

        self.quicksort(A, start, right)
        self.quicksort(A, left, end)

归并排序实现

class Solution:

    def sortIntegers(self, A):
        
        temp = [0 for _ in range len(A)]
        self.merge_sert(0, len(A) - 1, A, temp)

    def merge_sort(self, start, end, A, temp):

        if start >= end:
            return 

        mid = start + (end - start) // 2
        self.merge_sort(start, mid, A, temp)
        self.merge_sort(mid+1, end, A, temp)
        self.merge(start, mid, end, A, temp)

    def merge(self, start, mid, A, end, temp):
        left = start
        right = mid + 1
        index = start
        while left <= mid and right <= end:
            if A[left] < A[right]:
                temp[index] = A[left]
                left += 1
            else:
                temp[index] = A[right]
                right += 1

            index += 1

        while left <= mid:
            temp[index] = A[left]
            left += 1

        while right <= end:
            temp[index] = A[right]
            right += 1

        for index in range(start, end+1):
            A[index] = temp[index]

你可能感兴趣的:(整数数组前K大的问题)