1.消除游戏
题目:
给定一个从1 到 n 排序的整数列表。
首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。
第二步,在剩下的数字中,从右到左,从倒数第一个数字开始,每隔一个数字进行删除,直到列表开头。
我们不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
返回长度为 n 的列表中,最后剩下的数字。
思路:可以假定一个步长,和一个方向,每一次循环操作之后,步长相当于乘以2,去掉步长上的元素
时间复杂度O(n),空间复杂度O(1)
/**
* @param {number} n
* @return {number}
*/
var lastRemaining = function(n) {
//我们只需要记住每次删除数字后开头的数字是几就可以了
let flag = false;//false表示从左往右,true表示从右往左
let res = 1;
let step = 1;
let num = n;//剩下数字的个数
while(res+step<=n){
if(num%2===0){ res = flag===false?(res+step):res; }//剩下偶数个数字
else{//剩下奇数个数字
res +=step;
}
step = step*2;//更新步长
flag = !flag;//更新删除方向
//更新剩下数字的个数
num = Math.floor(num/2);
}
return res;
};
2.至少有K个重复字符的最长子串
题目:找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。
思路:每次遍历字符串,先找到所有出现次数少于k的字符,那么结果肯定是在以这些字符分隔的子字符串中,所以这是一个递归问题。在每一轮递归中记录那些字符的下标,然后分隔,进入下一轮递归
时间复杂度:O(n2),空间复杂度O(n)
/**
* @param {string} s
* @param {number} k
* @return {number}
*/
var longestSubstring = function(s, k) {
const l = s.length;
if (l < k) return 0;
const map = new Map();
for (const n of s) {
map.set(n, (map.get(n) || 0) + 1);
}
const res = [];
for (let i = 0; i < l; i++) {
if (map.get(s[i]) < k) {
res.push(i);
}
}
if (!res.length) return s.length;
const l1 = res.length;
const v = [];
v.push(longestSubstring(s.slice(0, res[0]), k));
for (let i = 1; i < l1; i++) {
v.push(longestSubstring(s.slice(res[i - 1] + 1, res[i]), k));
}
v.push(longestSubstring(s.slice(res[l1 - 1] + 1, l), k));
return Math.max(...v);
};
3.旋转函数
题目:
给定一个长度为 n 的整数数组 A 。
假设 Bk 是数组 A 顺时针旋转 k 个位置后的数组,我们定义 A 的“旋转函数” F 为:
F(k) = 0 * Bk[0] + 1 * Bk[1] + ... + (n-1) * Bk[n-1]。
计算F(0), F(1), ..., F(n-1)中的最大值。
思路:错位相减。
假定起始值是:a0 * 0 + a1* 1 + ...+ an-1 * n-1,那么旋转之后的下一个值是什么?
是a0*1 + a1*2 + ... + an-1*0,相对于上一个,可以假定右移,即每一个成员都加上自身,然后减去当前n*最后一个成员。
所以,先计算数组的累加和,然后在每一轮旋转时,加上累加和,减去这一轮的最后一个元素*数组长度
时间复杂度O(n),空间复杂度O(1)
/**
* @param {number[]} A
* @return {number}
*/
var maxRotateFunction = function(A) {
let sum=0; temp=0;
for (let i = 0; i < A.length; i++) {
temp += i * A[i];
sum += A[i];
}
let maxR = temp;
for (let i = A.length - 1; i > 0; i--) {
temp = sum - A.length * A[i] + temp;
maxR = temp > maxR ? temp : maxR;
}
return maxR
};
4.整数替换
题目:
给定一个正整数 n ,你可以做如下操作:
如果 n 是偶数,则用 n / 2替换 n 。
如果 n 是奇数,则可以用 n + 1或n - 1替换 n 。
n 变为 1 所需的最小替换次数是多少?
思路:用map记录每个数字对应的结果。如果n是偶数,那么map[n]=map[n/2]+1,如果n是奇数,那么map[n]=Math.min(map[n+1],map[n-1])+1,而map[n+1]和map[n-1]又可以用递归求,
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number} n
* @return {number}
*/
var integerReplacement = function(n) {
const map = {1:0};
const search = (n) => {
if (n == 1) return 0;
if (map[n]) return map[n];
if (n % 2) {
const v = 1 + Math.min(search(n + 1), search(n - 1));
map[n] = v;
} else {
const v = search(n / 2) + 1;
map[n] = v;
}
return map[n];
};
return search(n);
};
5.随机数索引
题目:
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
注意:
数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。
思路:记录每个数字的下标,用数组存放,然后随机获取数组下标
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number[]} nums
*/
var Solution = function (nums) {
this.map = new Map();
const l = nums.length;
for (let i = 0; i < l; i++) {
if (this.map.has(nums[i])) {
this.map.get(nums[i]).push(i);
} else {
this.map.set(nums[i], [i]);
}
}
};
/**
* @param {number} target
* @return {number}
*/
Solution.prototype.pick = function (target) {
const res = this.map.get(target);
const index = ~~(Math.random() * res.length);
return res[index];
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(nums)
* var param_1 = obj.pick(target)
*/