1.最长递增子序列
为了使序列上升尽可能的慢,不断记录长度为i的子序列中最大的数。 不断迭代dp[i]
/**
* @param {number[]} nums
* @return {number}
*/
var binarySearch = function(arr, target) {
let start = 0, end = arr.length - 1;
if (target == arr[start] || target == arr[end]) return -1;
if (target < arr[start]) return 0;
if (target > arr[end]) return end + 1;
while (start <= end) {
let middle = Math.floor((start + end) / 2);
if (target == arr[middle]) return -1;
if (target < arr[middle] && (middle == 0 || target > arr[middle-1])) {
return middle;
}
if (target > arr[middle]) {
start = middle + 1;
} else {
end = middle - 1;
}
}
return -1;
};
var lengthOfLIS = function(nums) {
let dp = [nums[0]];
for (let i = 1; i < nums.length; i++) {
let pos = binarySearch(dp, nums[i]);
if (pos != -1) {
if (pos == dp.length) {
dp.push(nums[i]);
} else {
dp[pos] = nums[i];
}
}
}
return dp.length;
};
- CPU调度使用的最小时间
题目:相同任务间有冷却时间。给定任务名和需要执行的次数,求怎样执行使得总时间最小。
解法:贪心。每个时间片选择,可以执行的任务中剩余次数最多的。
/**
* @param {character[]} tasks
* @param {number} n
* @return {number}
*/
//每个时间片内,选择不在冷冻期的剩余次数最多的任务
var leastInterval = function(tasks, n) {
let map = {}, nextTime;
//统计每个任务出现的频率,初始可执行时间为1
for (let i = 0; i < tasks.length; i++) {
if (!map[tasks[i]]) {
map[tasks[i]] = {
nextValidTime: 1,
rest: 1
};
} else {
map[tasks[i]].rest++;
}
}
//不遍历任务,只依次选择n个任务
for (let i = 0; i < tasks.length; i++) {
nextTime = Infinity;
//首先找到有可执行任务的最早时间
for (let taskName in map) {
if (map.hasOwnProperty(taskName) && map[taskName].rest > 0) {
if (map[taskName].nextValidTime < nextTime) {
nextTime = map[taskName].nextValidTime;
}
}
}
//找到可执行时间在nextTime时候的任务中的次数最多者
let nextTask = -1, maxRest = 0;
for (let taskName in map) {
if (map.hasOwnProperty(taskName) && map[taskName].rest > 0 ) {
if (map[taskName].nextValidTime == nextTime
&& map[taskName].rest > maxRest) {
maxRest = map[taskName].rest;
nextTask = taskName;
}
}
}
//已经找到了下一个任务,那么
//首先将该任务频次-1,其次将同任务下次开始时间全部调整为nextTime + n + 1,
//其他任务调整为nextTime + 1
for (let taskName in map) {
if (map.hasOwnProperty(taskName) && map[taskName].rest > 0) {
if (taskName == nextTask) {
map[taskName].rest--;
map[taskName].nextValidTime = nextTime + n + 1;
} else {
map[taskName].nextValidTime = Math.max(nextTime + 1,
map[taskName].nextValidTime);
}
}
}
}
//最后结果为nextTime
return nextTime;
};
数组移动:
- 找出重复数
在长度为n的数组nums中,所有数<n,找出重复数。
解法:将每一个数字移动到他的值-1,如果所在数与自己相同,那么重复,否则不重复。
线性循环,循环不变量是0~i-1的数各归其位
/**
* @param {number[]} nums
* @return {number[]}
*/
var findDuplicates = function(nums) {
let ans = [], i = 0;
while (i < nums.length) {
if (nums[i] == i + 1 || nums[i] == 0) {
i++;
} else {
let tmp = nums[nums[i] - 1];
if (tmp == nums[i]) {
ans.push(nums[i]);
nums[i] = 0;
} else {
nums[nums[i] - 1] = nums[i];
if (tmp != -1) {
nums[i] = tmp;
}
}
}
}
return ans;
};
两两淘汰
- 找出出现次数超过1/3的数
解法:保留大小为2窗口。遍历数组。 如果在窗口中,那么对应元素次数+1。 如果不在窗口中,有次数为0的元素,取代。 如果不在窗口中,无次数为0的元素,两数递减。
最后还要判断是否2个数真的出现了超过1/3次。