leetcode 347题

Posted by franki on November 18, 2020

leetcode 347题 前 K 个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2

输入: nums = [1], k = 1
输出: [1]

思路

利用小顶堆,前k个数就是小顶堆里面维护的数据

具体操作步骤:

  • 先遍历数据,用一个map记录下每一个元素出现的次数
  • 遍历map,先取前k个元素建堆
  • 到了第k个元素或者之后的元素,都需要与堆顶的元素做对比,如果比堆顶元素小则不作处理,如果比堆顶元素大则替换堆顶元素,并且重新堆化
  • 最后堆中的元素就是所需要求的前k个元素

代码

function topKFrequent(nums, k) {
    // 初始化堆
    const heap = [,];
    const map = new Map();

    // 先计算nums每个值出现的次数,用map存储
    nums.forEach(item => {
        if (map.has(item)) {
            map.set(item, map.get(item) + 1);
        } else {
            map.set(item, 1);
        }
    });

    // 如果元素数量小于k
    if (map.size <= k) {
        return [...map.keys()];
    }

    // 如果元素数量大于k,搭建小顶堆
    let i = 0;
    map.forEach((key, value) => {
        if (i < k) {
            // 取前k个,建堆
            heap.push(key);
            // 建立前k堆
            if (i === k-1) {
                buildHeap(heap, map, k);
            }
        } else if (map.get(heap[1]) < value) {
            // 替换并且重新堆化
            heap[1] = key;
            heapify(heap, map, k, 1);
        }
        i++;
    });

    heap.shift();
    return heap;
}

// 建堆
function buildHeap(heap, map, k) {
    if (k === 1) return;
    for (let i = Math.floor(k / 2); i>=1; i--) {
        heapify(heap, map, k, i);
    }
}

// 堆化
function heapify(heap, map, k, i) {
    // 自上而下堆化
    while (true) {
        let minIndex = i;
        if (2*i <= k && map.get(heap[2*i]) < map.get(heap[i])) {
            minIndex = 2*i;
        }
        if (2*i+1 <= k && map.get(heap[2*i+1]) < map.get(heap[minIndex])) {
            minIndex = 2*i+1;
        }
        if (i !== minIndex) {
            swap(heap, i, minIndex);
            i = minIndex;
        } else {
            break;
        }
    }
}

// 交换
function swap(arr, i, j) {
    [arr[i], arr[j]] = [arr[j], arr[i]];
}

复杂度分析

  • 时间复杂度: O(nlogn)
  • 空间复杂度: O(n)