topK问题研讨

本文介绍了两种常见问题的解决方案:如何在O(logK*N)时间内找到数组第k大/小元素,以及使用randomized select实现频率最高的k个元素。讲解了基于堆的top K算法和随机选择法的实现细节,适用于前端开发、后端开发等场景。

一:求数组第k大或者第k小

思路1:求数组第k大,构建一个含有k个元素的最小堆,对于新来的元素,若当前元素大于栈顶元素,入战并弹出站顶元素,若当前元素大小于栈顶元素,不处理。
time complexity:logK * N
memory complexity :O(K)

public int findKthLargest(int[] nums, int k) {
		 final PriorityQueue<Integer> pq = new PriorityQueue<>();
    for(int val : nums) {
        pq.offer(val);

        if(pq.size() > k) {
            pq.poll();
        }
    }
    return pq.peek();
		
	}

思路2:对选择排序进行改造,对partition(快速排序用到)进行修改
O(N) best case / O(N^2) worst case running time
O(1) memory

 public int findKthLargest(int[] nums, int k) {
        return quickSort(nums, 0, nums.length -1, k);
    }

    private int quickSort(int[] nums, int s, int e, int k) {
        int in = partition(nums, s, e);
        if (in == k - 1) {
            return nums[in];
        } else if (in > k - 1) {
            return quickSort(nums, s, in - 1, k);
        } else {
            return quickSort(nums, in + 1, e, k);
        }

    }

    private int partition(int[] nums, int s, int e) {
        int p = nums[e], left = s - 1, right = e + 1, begin = s;
        while (begin < right) {
            if (nums[begin] > p) {
                swap(nums, ++left, begin++);
            } else if(nums[begin] < p){
                swap(nums, --right, begin);
            } else {
                begin++;
            }
        }
        return begin-1;
    }

    private void swap(int[] nums, int p, int k) {
        if (p != k) {
            int t = nums[p];
            nums[p] = nums[k];
            nums[k] = t;
        }

    }

2:求数组k个最大或者最小
思路1: 构建一个含有k个元素的最小堆,对于新来的元素,若当前元素大于栈顶元素,入战并弹出站顶元素,若当前元素大小于栈顶元素,不处理,最后堆中所有元素就是最终结果
time complexity:logK * N
memory complexity :O(K)

//求最大的k个
    private static int[] topKByHeap(int[] a, int k) {
        PriorityQueue<Integer> queue = new PriorityQueue();   // 小根堆
        for (int i = 0; i < a.length; i++) {
            if(queue.size() < k) {
                queue.add(a[i]);
            } else {
               if(a[i] > queue.peek()) {
                 queue.poll();
                 queue.add(a[i]);
               }
            }
        }
        int[] res = new int[k];
        int i = k-1;
        while (!queue.isEmpty()) {
            res[i--] = queue.poll();
        }
        return res;
    }

思路2:随机选择 (randomized select) 最为经典,用减治法 (Reduce & Conquer) 的思想,将数据规模急速降低,总体复杂度为O(n)
在这里插入图片描述
代码:` public static int[] findKthLargest(int[] nums, int k) {
int lo = 0;
int hi = nums.length - 1;
while (lo < hi) {
final int j = partition(nums, lo, hi);
if (j +1 < k) {
lo = j + 1;
} else if (j +1 > k) {
hi = j - 1;
} else {
break;
}
}
return Arrays.copyOf(nums, k);
}

private static int partition(int[] nums, int s, int e) {
    int p = nums[e], left = s - 1, right = e + 1, begin = s;
    while (begin < right) {
        if (nums[begin] > p) {
            exch(nums, ++left, begin++);
        } else if (nums[begin] < p) {
            exch(nums, --right, begin);
        } else {
            begin++;
        }
    }
    return begin - 1;
}

private static void exch(int[] a, int i, int j) {
    if (i != j) {
        final int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

}`

TopK frequent

求数组中出现频率最高的k个元素,其实这种问题和原始topK问题性质一样,只是在比较的时候需要按照出现次数进行比较,这里按照小根堆的思路,实现如下代码:
leetcode题目

 public int[] topKFrequent(int[] nums, int k) {
          Map<Integer, Integer> map = new HashMap<>(16);
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        // 遍历map,用最小堆保存频率最大的k个元素
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return map.get(a) - map.get(b);
            }
        });
        for (Integer key : map.keySet()) {
            if (pq.size() < k) {
                pq.add(key);
            } else if (map.get(key) > map.get(pq.peek())) {
                pq.remove();
                pq.add(key);
            }
        }

        // 取出最小堆中的元素
        int[] ret = new int[k];
        int i = 0;
        while (!pq.isEmpty()) {
            ret[i++] = pq.remove();
        }

        return ret; 
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值