2019 算法面试相关(leetcode)--优先队列

0.453字数 1433阅读 814

2019 iOS面试题大全---全方面剖析面试
2018 iOS面试题---算法相关
1、七种常见的数组排序算法整理(C语言版本)
2、2019 算法面试相关(leetcode)--数组和链表
3、2019 算法面试相关(leetcode)--字符串
4、2019 算法面试相关(leetcode)--栈和队列
5、2019 算法面试相关(leetcode)--优先队列
6、2019 算法面试相关(leetcode)--哈希表
7、2019 算法面试相关(leetcode)--树、二叉树、二叉搜索树
8、2019 算法面试相关(leetcode)--递归与分治
9、2019 算法面试相关(leetcode)--贪心算法
10、2019 算法面试相关(leetcode)--动态规划(Dynamic Programming)
11、2019 算法面试相关(leetcode)--动态规划之背包问题


优先队列(priority queue)

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。
优先队列是一种十分强大的数据结构,它保持了一种动态的有序性,对于不断改变有入队的操作,而又需要某种最大或最小的操作的问题是再合适不过了,通常优先队列的实现是由最小堆或者最大堆完成的,并通过堆排序保持队列的有序性,模拟队列的结构。

一、 数据流中的第K大元素
设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。

你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。

示例:

int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8


从题目上可以看出,我们可以使用优先队列来解题,创建一个长度为k的有序队列,每次添加新元素,只需和队首元素n对比即可,如果小于n,则不入队,大于n,则n出队,新元素按顺序插入到队列中,因为队列是有序的,插入可以使用二分法。则第K大元素则固定是队首的元素

/**
 * @param {number} k
 * @param {number[]} nums
 */
var KthLargest = function(k, nums) {

    this.nums = nums.sort((a, b) => a - b)

    if(nums.length >= k) this.nums = this.nums.slice(nums.length - k,nums.length)

    this.k = k
};

/** 
 * @param {number} val
 * @return {number}
 */
KthLargest.prototype.add = function(val) {
    
    if(!this.nums.length || (this.nums.length < this.k && val <= this.nums[0])) this.nums.unshift(val)

    else if(val > this.nums[0]){

       if(this.nums.length >= this.k) this.nums.shift()

        let l = 0,r = this.nums.length - 1

        while(l <= r){

            let m = Math.floor((l + r)/2)

            if(this.nums[m] < val) l = m + 1

            else r = m - 1
        }

        this.nums.splice(l,0,val)
    }

    return this.nums[0]
};

二、 滑动窗口最大值
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。

返回滑动窗口最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
注意:

你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。

进阶:

你能在线性时间复杂度内解决此题吗?


很明显,这也是一个优先队列的题目,滑动窗口就是一个优先队列,我们可以维护一个滑动窗口,长度不定,如果后进的元素n大于队列尾的元素m,由于n后进,根据队列先进先出的特性,n也会比m后出,那么m就可以直接移除出队列了,因为m比n小,则队列内最大元素怎么也不会是n。同理,队列内所有比n小的元素都可以移除了,然后n入队即可。最大元素则固定是队首元素。

var maxSlidingWindow = function(nums, k) {
    
    if(!nums || !nums.length) return []

    let win = []

    let result = []

    for(let i = 0; i < nums.length; i++){

        if(i >= k && win[0] <= i - k) win.splice(0,1)

        while(win.length && nums[i] >= nums[win[win.length - 1]]){

            win.pop()
        }

        win.push(i)
        
        if(i >= k - 1) result.push(nums[win[0]])
    }

    return result
};

三、 前K个高频单词

给一非空的单词列表,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

示例 1:

输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 "i" 在 "love" 之前。

示例 2:

输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,
出现次数依次为 4, 3, 2 和 1 次。

注意:

假定 k 总为有效值, 1 ≤ k ≤ 集合元素数。
输入的单词均由小写字母组成。

扩展练习:

尝试以 O(n log k) 时间复杂度和 O(n) 空间复杂度解决。


首先我们先把数组中的单词和个数存在哈希表中,然后题目就和上边问题一很相像了,也是用优先队列来解决

/**
 * @param {string[]} words
 * @param {number} k
 * @return {string[]}
 */
var topKFrequent = function(words, k) {
    
    let q = []

    let dic = {}

    for (const ch of words.sort()) {
        
        dic[ch] = 1 + (dic[ch] || 0)
    }

    for (const key in dic) {
        
        if(!q.length || (q.length < k && dic[key] <= dic[q[q.length - 1]])) q.push(key)

        else if(dic[key] > dic[q[q.length - 1]]){

            if(q.length >= k) q.pop()

            let l = 0,r = q.length - 1

            while(l <= r){

                let m = Math.floor((l + r)/2)
    
                if(dic[q[m]] >= dic[key]) l = m + 1
    
                else r = m - 1
            }

            q.splice(l,0,key)
        }
    }

    return q
};

推荐阅读更多精彩内容