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
};