1.任务调度器
题目:
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间 。
思路:因为有冷却时间,所以任务执行的最短时间是n,最长时间取决于次数最多的任务。先计算任务出现的次数及出现最多的任务和数量,只需要考虑这些任务的执行。假设最多次数的任务,出现次数是k,有m个最多任务。那么时间就是(m-1)*(n+1)+k。然后取二者的较大值
时间复杂度O(n),空间复杂度O(n)
/**
* @param {character[]} tasks
* @param {number} n
* @return {number}
*/
var leastInterval = function(tasks, n) {
if (!n) return tasks.length;
const map = new Map();
let max = 0;
let list = [];
for (const k of tasks) {
map.set(k, (map.get(k) || 0) + 1);
if (max < map.get(k)) {
max = map.get(k);
list.splice(0, Infinity, k);
} else if (max === map.get(k)) {
list.push(k);
}
}
return Math.max((max - 1) * (n + 1) + list.length,tasks.length);
};
2.设计循环队列
题目:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
思路:用数组即可
var MyCircularQueue = function (k) {
this.list = [];
this.max = k;
};
/**
* @param {number} value
* @return {boolean}
*/
MyCircularQueue.prototype.enQueue = function (value) {
if (this.list.length >= this.max) return false;
this.list.push(value);
return true;
};
/**
* @return {boolean}
*/
MyCircularQueue.prototype.deQueue = function () {
if (!this.list.length) return false;
this.list.shift();
return true
};
/**
* @return {number}
*/
MyCircularQueue.prototype.Front = function () {
if (!this.list.length) return -1;
return this.list[0];
};
/**
* @return {number}
*/
MyCircularQueue.prototype.Rear = function () {
if (!this.list.length) return -1;
return this.list[this.list.length - 1];
};
/**
* @return {boolean}
*/
MyCircularQueue.prototype.isEmpty = function () {
return !this.list.length;
};
/**
* @return {boolean}
*/
MyCircularQueue.prototype.isFull = function () {
return this.list.length === this.max;
};
3.在二叉树中增加一行
题目:
给定一个二叉树,根节点为第1层,深度为 1。在其第 d 层追加一行值为 v 的节点。
添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创建两个值为 v 的左子树和右子树。
将 N 原先的左子树,连接为新节点 v 的左子树;将 N 原先的右子树,连接为新节点 v 的右子树。
如果 d 的值为 1,深度 d - 1 不存在,则创建一个新的根节点 v,原先的整棵树将作为 v 的左子树。
思路:递归。利用层序遍历的方式。先判断是否是跟节点,然后遍历节点,记录该节点的层级k,如果k===d-1,那么该节点就是需要插入的节点的父节点
时间复杂度O(n),空间复杂度O(h)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} v
* @param {number} d
* @return {TreeNode}
*/
var addOneRow = function(root, v, d) {
if (d < 2) {
const node = new TreeNode(v);
node.left = root;
return node;
}
const search = (root, level) => {
if (!root) return;
if (level === d - 1) {
const left = root.left;
const right = root.right;
const leftNode = new TreeNode(v);
const rightNode = new TreeNode(v);
leftNode.left = left;
rightNode.right = right;
root.left = leftNode;
root.right = rightNode;
} else {
search(root.left, level + 1);
search(root.right, level + 1);
}
return root;
};
return search(root, 1);
};
4.函数的独占时间
题目:
给出一个非抢占单线程CPU的 n 个函数运行日志,找到函数的独占时间。
每个函数都有一个唯一的 Id,从 0 到 n-1,函数可能会递归调用或者被其他函数调用。
日志是具有以下格式的字符串:function_id:start_or_end:timestamp
。例如:"0:start:0"
表示函数 0 从 0 时刻开始运行。"0:end:0"
表示函数 0 在 0 时刻结束。
函数的独占时间定义是在该方法中花费的时间,调用其他函数花费的时间不算该函数的独占时间。你需要根据函数的 Id 有序地返回每个函数的独占时间。
思路:这题开始想的是递归的思路。如果遇到两个start,就进入递归,否则在同一层处理。
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number} n
* @param {string[]} logs
* @return {number[]}
*/
const findIndex = (logs, index, id) => {
let c = 0;
for (let i = index; i < logs.length; i++) {
let [cid, flag, t] = logs[i];
if (cid !== id) continue;
if (flag === "start") {
c++;
} else {
c--;
}
if (!c) return i + 1;
}
};
var exclusiveTime = function (n, logs) {
const time = new Array(n).fill(0);
logs = logs.map((item) => {
const res = item.split(":");
res[2] = Number(res[2]);
return res;
});
const search = (logs) => {
const l = logs.length;
let cur = 0;
const stack = [];
for (let i = 0; i < l; i) {
let [id, flag, t] = logs[i];
if (flag === "end") {
const curTime = Number(t) - stack.pop() - cur + 1;
time[id] += curTime;
i++;
} else if (flag === "start" && stack.length) {
const index = findIndex(logs, i, id);
search(logs.slice(i, index));
const curTime = logs[index - 1][2] - logs[i][2] + 1;
cur += curTime;
i = index;
} else {
stack.push(t);
i++;
}
}
};
search(logs);
return time;
};
然后,利用栈的思想,记录start的任务下标。如果栈内有超过一个start,那么,start之间的时间就是栈顶任务的独占时间。所以只需要一次遍历,用一个遍历记录上个节点的时间即可
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number} n
* @param {string[]} logs
* @return {number[]}
*/
var exclusiveTime = function (n, logs) {
const time = new Array(n).fill(0);
let pre = 0;
const stack = [];
for (let log of logs) {
let [id, flag, t] = log.split(":");
t = Number(t);
if (flag === "start") {
if (stack.length) {
time[stack[stack.length - 1]] += t - pre - 1;
}
stack.push(id);
pre = t;
} else {
stack.pop();
time[id] += t - pre + 1;
pre = t;
}
}
return time;
};
换个角度,对于一个任务的开始和结束,每次都加,这样存在嵌套,就需要减去嵌套的时间。
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number} n
* @param {string[]} logs
* @return {number[]}
*/
var exclusiveTime = function (n, logs) {
const time = new Array(n).fill(0);
logs = logs.map((item) => {
const res = item.split(":");
res[2] = Number(res[2]);
return res;
});
const stack = [];
let preStart = [];
for (const log of logs) {
const [id, flag, t] = log;
if (flag === "start") {
stack.push(id);
preStart.push(t);
} else {
stack.pop();
const duration = t - preStart.pop() + 1;
time[id] += duration;
if (stack.length) {
time[stack[stack.length - 1]] -= duration;
}
}
}
return time;
};
5.大礼包
题目:
在LeetCode商店中, 有许多在售的物品。
然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。
现给定每个物品的价格,每个大礼包包含物品的清单,以及待购物品清单。请输出确切完成待购清单的最低花费。
每个大礼包的由一个数组中的一组数据描述,最后一个数字代表大礼包的价格,其他数字分别表示内含的其他种类物品的数量。
任意大礼包可无限次购买。
思路:递归+回溯。用map记录每一种组合花费的金额,在遍历礼包时,如果礼包不能加入组合,就跳过,加入,就和之前同样的组合比较
/**
* @param {number[]} price
* @param {number[][]} special
* @param {number[]} needs
* @return {number}
*/
var shoppingOffers = function(price, special, needs) {
let map = new Map();
let f = function (curNeeds, preCost) {
let key = curNeeds.join("-") + "_" + preCost;
if (map.has(key)) return map.get(key);
let ans = Infinity;
for (let present of special) {
let flag = true;
let newNeeds = new Array(curNeeds.length);
for (let j = 0; j < curNeeds.length; j++) {
newNeeds[j] = curNeeds[j] - present[j];
if (present[j] > curNeeds[j]) {
flag = false;
break;
}
}
if (flag) {
ans = Math.min(ans, f(newNeeds, preCost + present[present.length - 1]));
}
}
let sum = 0;
for (let i = 0; i < curNeeds.length; i++) {
sum += price[i] * curNeeds[i];
}
ans = Math.min(ans, preCost + sum);
map.set(key, ans);
return ans;
};
return f(needs, 0);
};