75.前 K 个高频元素

69 阅读2分钟

题目链接

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

解法1 堆排序

思路

返回频率最高的几个元素,首先需要的肯定一个频率表,所以需要先给频次计数。

接下来就是入堆了,这里使用二维数组来模拟(C++ 可以用 pair),保存数值和频次。最后将这个二维数组返回为一维即可。

代码

function topKFrequent(nums: number[], k: number): number[] {
    const freqMap = new Map<number, number>();

    // 1. 统计频率
    for (const num of nums) {
        freqMap.set(num, (freqMap.get(num) || 0) + 1);
    }

    // 2. 定义最小堆:存储 [num, freq] 对
    const heap: [number, number][] = [];

    const pushHeap = (item: [number, number]) => {
        heap.push(item);
        let i = heap.length - 1;
        while (i > 0) {
            const parent = Math.floor((i - 1) / 2);
            if (heap[parent][1] <= heap[i][1]) break;
            [heap[parent], heap[i]] = [heap[i], heap[parent]];
            i = parent;
        }
    };

    const popHeap = (): [number, number] => {
        const top = heap[0];
        const last = heap.pop()!;
        if (heap.length === 0) return top;
        heap[0] = last;
        let i = 0;
        while (true) {
            let left = 2 * i + 1;
            let right = 2 * i + 2;
            let smallest = i;
            if (left < heap.length && heap[left][1] < heap[smallest][1]) smallest = left;
            if (right < heap.length && heap[right][1] < heap[smallest][1]) smallest = right;
            if (smallest === i) break;
            [heap[i], heap[smallest]] = [heap[smallest], heap[i]];
            i = smallest;
        }
        return top;
    };

    // 3. 维护堆中只保留前 k 个频率最高的元素
    for (const [num, freq] of freqMap.entries()) {
        pushHeap([num, freq]);
        if (heap.length > k) {
            popHeap(); // 移除最小频率
        }
    }

    return heap.map(([num]) => num);
};

时空复杂度

时间复杂度:O(n logk)

空间复杂度:O(n + k)

解法2 桶排序

思路

桶排序的思路比堆排序稍微好理解一点,就是模拟 n 个桶,每个索引为频次,然后从大的索引往小的索引遍历,如果桶里有值,那就放到结果当中。

代码

function topKFrequent(nums: number[], k: number): number[] {
    const freqMap = new Map<number, number>();

    // 1. 统计每个元素出现的频率
    for (const num of nums) {
        freqMap.set(num, (freqMap.get(num) || 0) + 1);
    }

    // 2. 初始化桶数组:索引为频率,值为出现这些频率的数
    const buckets: number[][] = Array(nums.length + 1).fill(0).map(() => []);

    for (const [num, freq] of freqMap.entries()) {
        buckets[freq].push(num);
    }

    // 3. 从后往前遍历桶,收集频率最高的 k 个元素
    const result: number[] = [];

    for (let i = buckets.length - 1; i >= 0 && result.length < k; i--) {
        if (buckets[i].length > 0) {
            result.push(...buckets[i]);
        }
    }

    return result.slice(0, k);
};

时空复杂度

时间复杂度:O(n)

空间复杂度:O(n)