数学算法
涉及到数学的算法,基本都有固定的算法公式,如果知道就知道,不知道也不好想出来,比如求质数,进制转换等
这些题目首先你得了解这些数学知识,然后才能做,不然根本没有任何思绪。
9.1 最小公倍数
最小公倍数就是能够整除两个数的最小数
1 首先求两个数的最大公因数,然后再把两个数相乘再除以最大公因数就可以了,也就是辗转相除法。
求最大公因数代码如下:
const gcd = (a, b) => {
return b === 0 ? a : gcd(b, a % b);
};
求最大公倍数代码如下:
const gcd = (a, b) => {
return b === 0 ? a : gcd(b, a % b);
};
export default (a, b) => {
return Math.floor((a * b) / gcd(a, b));
};
9.2 质数
质数又称素数,指的是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然 数。值得注意的是,每一个数都可以分解成质数的乘积。
204 计算质数的数量
题目描述
给定一个数字 n,求小于 n 的质数的个数。
例子1
Input: n = 10
output: 4
解释:
2, 3, 5, 7 是小于10的质数
例子2
Input: 0
output: 0
例子3
Input: 1
output: 0
思考
1 直接使用暴力解法就可以
2 使用一些技巧,这里涉及很多数学特性,不过也很简单,看下代码就可以了
参考实现1
参考实现2
实现1
/**
* @param {number} n
* @return {number}
*/
// Runtime: 124 ms, faster than 92.38% of JavaScript online submissions for Count Primes.
// Memory Usage: 52.1 MB, less than 45.59% of JavaScript online submissions for Count Primes.
export default (n) => {
const notPrime = new Array(n).fill(0);
let count = 0;
for (let i = 2; i < n; i++) {
if (notPrime[i] === 0) {
count++;
// 找到另外一个因子,如果存在另外一个因子,则不是质数
for (let j = 2; i * j < n; j++) {
notPrime[i * j] = 1;
}
}
}
return count;
};
实现2
/**
* @param {number} n
* @return {number}
*/
// Runtime: 120 ms, faster than 94.49% of JavaScript online submissions for Count Primes.
// Memory Usage: 52.1 MB, less than 45.59% of JavaScript online submissions for Count Primes.
export default (n) => {
const excludes = new Array(n).fill(false);
if (n < 3) return 0;
// 最大的素数,因为偶数都不是质数
let maxCount = Math.floor(n / 2);
// 如果奇数相乘大于n,所以肯定不存在
for (let i = 3; i * i < n; i += 2) {
//说明已经排除了
if (excludes[i]) {
continue;
}
// 比如3,那么9,15,21肯定不是质数
for (let j = i * i; j < n; j += i * 2) {
if (!excludes[j]) {
excludes[j] = true;
maxCount--;
}
}
}
return maxCount;
};
504. 七进制数
题目描述
给定一个整数,将其转化为7进制,并以字符串形式输出。
例子1
Input: 100
output: "202"
例子2
Input: -7
output: 10
思考
1 固定的转换套路,很简单
参考实现1
实现1
/**
* @param {number} num
* @return {string}
*/
// Runtime: 76 ms, faster than 90.91% of JavaScript online submissions for Base 7.
// Memory Usage: 39 MB, less than 13.29% of JavaScript online submissions for Base 7.
export default (num) => {
if (num == 0) return "0";
const is_negative = num < 0;
if (is_negative) {
num = -num;
}
let res = "";
while (num > 0) {
const a = Math.floor(num / 7);
const b = num % 7;
res = b + res;
num = a;
}
return is_negative ? "-" + res : res;
};
时间复杂度O(n)空间复杂度O(1)
168. Excel表列名称
题目描述
给定一个正整数,返回它在 Excel 表中相对应的列名称。
例如:
1 -> A
2 -> B
3 -> C
...
26 -> Z
27 -> AA
28 -> AB
例子1
Input: 1
output: "A"
例子2
Input: 28
output: "AB"
例子3
Input: 701
output: "ZY"
思考
1 这题和上面差不多,也是类似数制转换的问题,也就是转换成26进制,但是这里会涉及到0的处理,所以得想办法处理0的问题
这里有一种是使用数组处理0,因为如果发现是0的话,必须上高位借1。
另外需要主要的是js的相除会有可能是小数,所以得向下取整
参考实现1
2 然后还有一种使用递归的方法,该方法有一个奇妙的地方就是为了避免处理0,是首先把n减去1,然后再处理
参考实现2
3 这里是把递归改成不递归
参考实现3
实现1
/**
* @param {number} n
* @return {string}
*/
const map = {
0: "0",
1: "A",
2: "B",
3: "C",
4: "D",
5: "E",
6: "F",
7: "G",
8: "H",
9: "I",
10: "J",
11: "K",
12: "L",
13: "M",
14: "N",
15: "O",
16: "P",
17: "Q",
18: "R",
19: "S",
20: "T",
21: "U",
22: "V",
23: "W",
24: "X",
25: "Y",
26: "Z",
};
// Runtime: 76 ms, faster than 70.41% of JavaScript online submissions for Excel Sheet Column Title.
// Memory Usage: 38.6 MB, less than 23.60% of JavaScript online submissions for Excel Sheet Column Title.
export default (n) => {
if (n <= 26) return map["" + n];
let res = [];
while (n > 26) {
let a = Math.floor(n / 26);
let b = n % 26;
res.unshift(b);
n = a;
}
res.unshift(n);
for (let i = 1; i < res.length; i++) {
if (res[i] === 0) {
let j = i - 1;
res[i] = 26;
res[j]--;
while (res[j] === 0 && j >= 1 && res[j - 1] > 0) {
res[j] = 26;
res[j - 1]--;
j--;
}
}
}
let k = 0;
while (res[k] === 0) {
res.shift();
k++;
}
console.log(res);
const temp = res
.map((item) => {
return map["" + item];
})
.join("");
return temp;
};
实现2
/**
* @param {number} n
* @return {string}
*/
const convertToTitle = (n) => {
if (n === 0) return "";
return convertToTitle(Math.floor(--n / 26)) + String.fromCharCode("A".charCodeAt() + (n % 26));
};
export default convertToTitle;
实现3
/**
* @param {number} n
* @return {string}
*/
// Runtime: 72 ms, faster than 88.06% of JavaScript online submissions for Excel Sheet Column Title.
// Memory Usage: 38.2 MB, less than 73.13% of JavaScript online submissions for Excel Sheet Column Title.
export default (n) => {
if (n === 0) return "";
let res = "";
while (n > 0) {
--n;
const a = Math.floor(n / 26);
const b = n % 26;
res = String.fromCharCode("A".charCodeAt() + b) + res;
n = a;
}
return res;
};
172. 阶乘后的零
题目描述
给定一个整数 n,返回 n! 结果尾数中零的数量。
例子1
Input: 3
output: 0
例子2
Input: 5
output: 1
解释:5!=120
思考
1 刚开始的时候想直接使用暴力解法,但是发现如果输入30之后,就会用科学计数法来表示,基本上就得不到正确的结果了
2 另外一种思路是可以发现10!= 362800,这时候我们要求出最后有几个0,很明显就是看362800可以除以10得到正整数。
此时已经发现先得到10的结果再求多少个0已经得不到正确的结果了,可以想一下10!还可以怎么表示?
10! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10
那么10!/ 10 就可以表示成1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 / 10
同时除以10可以看成除以2 * 5,同时10!也可以转换成
1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 2 * 5,那么公式就变成了1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 2 * 5/ 2 * 5
此时我们基本上就可以发现其实就是求10!里边含有多少对2 * 5了
那么如果求呢?
这里可以发现10!里边肯定含有的2比5多,因为任何一个偶数都含有2,但是不一定含有5
所以我们只需要求5出现了多少次就可以了
那么怎么求5出现了多少次呢?
我们可以分成几步来求
第一步求含有1个5的数量,比如在10!中那就是5 和 10
第二步求含有2个5的数量,比如在30!中那就是25
依次类推
参考实现1
实现1
/**
* @param {number} n
* @return {number}
*/
// Runtime: 88 ms, faster than 68.48% of JavaScript online submissions for Factorial Trailing Zeroes.
// Memory Usage: 39.4 MB, less than 43.21% of JavaScript online submissions for Factorial Trailing Zeroes.
const trailingZeroes = (n) => {
if (n <= 4) return 0;
let res = 0;
while (n > 0) {
n = Math.floor(n / 5);
res += n;
}
return res;
};
export default trailingZeroes;
时间复杂度O(lgn)空间复杂度O(1)
415. 字符串相加
题目描述
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。
提示:
1 num1 和num2 的长度都小于 5100
2 num1 和num2 都只包含数字 0-9
3 num1 和num2 都不包含任何前导零
4 你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式
思考
1 题目比较简单,直接使用charCodeAt获取字符的值就可以
参考实现1
实现1
/**
* @param {string} num1
* @param {string} num2
* @return {string}
*/
// Runtime: 76 ms, faster than 98.86% of JavaScript online submissions for Add Strings.
// Memory Usage: 40.9 MB, less than 47.14% of JavaScript online submissions for Add Strings.
export default (num1, num2) => {
const len1 = num1.length;
const len2 = num2.length;
let len = len1;
const maxLen = Math.max(len1, len2);
if (len2 < len1) {
const temp = num2;
num2 = num1;
num1 = temp;
len = len2;
}
let flag = 0;
const res = [];
for (let i = len - 1; i >= 0; i--) {
const sum = num1.charCodeAt(i) + num2.charCodeAt(maxLen - len + i) - 96 + flag;
if (sum >= 10) {
res.unshift(sum % 10);
flag = 1;
} else {
res.unshift(sum);
flag = 0;
}
}
if (maxLen > len) {
for (let i = maxLen - 1 - len; i >= 0; i--) {
const sum = num2.charCodeAt(i) - 48 + flag;
if (sum >= 10) {
res.unshift(sum % 10);
flag = 1;
} else {
res.unshift(sum);
flag = 0;
}
}
}
if (flag === 1) {
res.unshift(1);
}
return res.join("");
};
时间复杂度O(Math.max(len1,len2)) 空间复杂度O(len2)
67. 二进制求和
题目描述
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。
提示:
1 每个字符串仅由字符 '0' 或 '1' 组成。
2 1 <= a.length, b.length <= 10^4
3 字符串如果不是 "0" ,就都不含前导零。
例子1
input:a = "11", b = "1"
output:"100"
例子2
input:a = "1010", b = "1011"
output:"10101"
思考
1 和415题目一样的逻辑
参考实现1
实现1
/**
* @param {string} a
* @param {string} b
* @return {string}
*/
// Runtime: 92 ms, faster than 53.83% of JavaScript online submissions for Add Binary.
// Memory Usage: 40.4 MB, less than 66.69% of JavaScript online submissions for Add Binary.
export default (a, b) => {
const len1 = a.length;
const len2 = b.length;
let len = len1;
const maxLen = Math.max(len1, len2);
if (len2 < len1) {
const temp = b;
b = a;
a = temp;
len = len2;
}
let flag = 0;
const res = [];
for (let i = len - 1; i >= 0; i--) {
const sum = a.charCodeAt(i) + b.charCodeAt(maxLen - len + i) - 96 + flag;
if (sum === 2) {
res.unshift(0);
flag = 1;
} else if (sum === 3) {
res.unshift(1);
flag = 1;
} else {
res.unshift(sum);
flag = 0;
}
}
if (maxLen > len) {
for (let i = maxLen - 1 - len; i >= 0; i--) {
const sum = b.charCodeAt(i) - 48 + flag;
if (sum === 2) {
res.unshift(0);
flag = 1;
} else if (sum === 3) {
res.unshift(1);
flag = 1;
} else {
res.unshift(sum);
flag = 0;
}
}
}
if (flag === 1) {
res.unshift(1);
}
return res.join("");
};
时间复杂度O(Math.max(len1,len2)) 空间复杂度O(len2)
9.5 随机与取样
384. 打乱数组和恢复
题目描述
给定一个数组,要求实现两个指令函数。第一个函数“shuffle”可以随机打乱这个数组,第 二个函数“reset”可以恢复原来的顺序。
例子1
input:nums = [1,2,3], actions: ["shuffle","shuffle","reset"]
output:[[2,1,3],[3,2,1],[1,2,3]]
思考
1 题目很简单,就是一个简单的洗牌算法,不过面试中经常会被问到。
参考实现1
实现1
/**
* @param {number[]} nums
*/
// Runtime: 236 ms, faster than 67.68% of JavaScript online submissions for Shuffle an Array.
// Memory Usage: 52.4 MB, less than 55.56% of JavaScript online submissions for Shuffle an Array.
var Solution = function (nums) {
this.nums = nums || [];
};
/**
* Resets the array to its original configuration and return it.
* @return {number[]}
*/
Solution.prototype.reset = function () {
return this.nums;
};
/**
* Returns a random shuffling of the array.
* @return {number[]}
*/
Solution.prototype.shuffle = function () {
const tempNums = [...this.nums];
const len = tempNums.length;
for (let i = 0; i < tempNums.length; i++) {
const index = Math.floor(Math.random() * (len - i) + i);
// console.log(index)
const temp = tempNums[index];
tempNums[index] = tempNums[i];
tempNums[i] = temp;
}
return tempNums;
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(nums)
* var param_1 = obj.reset()
* var param_2 = obj.shuffle()
*/
时间复杂度O(n)
空间复杂度O(n)
528. 按权重随机选择
题目描述
给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。
也就是说,选取下标 i 的概率为 w[i] / sum(w) 。
例子1
input:["Solution","pickIndex"]
[[[1]],[]]
output:[null,0]
解释:Solution solution = new Solution([1]);
solution.pickIndex(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。
例子2
input:["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
output:[null,1,1,1,1,0]
解释:Solution solution = new Solution([1, 3]);
solution.pickIndex(); // 返回 1,返回下标 1,返回该下标概率为 3/4 。
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 0,返回下标 0,返回该下标概率为 1/4 。
由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
......
诸若此类。
思考
1 刚开始想按照次数,比如当输入[1,3]的时候,如果选择4次,一次输出1,一次输出3,但是发现这样不行,这样就不是随机的了
后来看了下题解,其实道理很简单,就是使用前缀和,搞成一个类似于区间的
然后使用随机数,看落在那个区间,刚好符合概率
参考实现1
还可以使用二分法查找,参考实现2
实现1
/**
* @param {number[]} w
*/
var Solution = function (w) {
const len = w.length;
this.chances = new Array(len).fill(0);
const sum = w.reduce((a, b) => a + b);
for (let i = 0; i < w.length; i++) {
w[i] += i === 0 ? 0 : w[i - 1];
this.chances[i] = w[i] / sum;
}
};
/**
* @return {number}
*/
Solution.prototype.pickIndex = function () {
if (this.chances.length === 1) {
return 0;
}
const random = Math.random().toFixed(2);
// console.log(this.chances);
for (let i = 0; i < this.chances.length; i++) {
if (random <= this.chances[i]) {
return i;
}
}
return this.chances.length - 1;
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(w)
* var param_1 = obj.pickIndex()
*/
export default Solution;
实现2
/**
* @param {number[]} w
*/
var Solution = function (w) {
const len = w.length;
this.chances = new Array(len).fill(0);
const sum = w.reduce((a, b) => a + b);
for (let i = 0; i < w.length; i++) {
w[i] += i === 0 ? 0 : w[i - 1];
this.chances[i] = w[i] / sum;
}
};
/**
* @return {number}
*/
Solution.prototype.pickIndex = function () {
if (this.chances.length === 1) {
return 0;
}
const random = Math.random().toFixed(2);
let low = 0;
let high = this.chances.length - 1;
while (low <= high) {
const mid = Math.floor(low + (high - low) / 2);
if (this.chances[mid] >= random) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return low;
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(w)
* var param_1 = obj.pickIndex()
*/
export default Solution;
382. 链表随机节点
题目描述
给定一个单链表,随机选择链表的一个节点,并返回相应的节点值。保证每个节点被选的概率一样。
进阶:
如果链表十分大且长度未知,如何解决这个问题?你能否使用常数级空间复杂度实现?
例子1
// 初始化一个单链表 [1,2,3].
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
Solution solution = new Solution(head);
// getRandom()方法应随机返回1,2,3中的一个,保证每个元素被返回的概率相等。
思考
1 基本的可以使用数组存储整个链表,然后直接随机就可以了
2 后来看了下这里是典型的水库算法
水库算法比较简单
当遇到第一个节点的时候,我们选择第一个节点
当遇到第二个节点的时候,这个时候要么替换第一个节点,要么不替换第一个节点,如果替换第一个节点的时候,也是1/2
当遇到第三个节点的时候,如果替换那么概率是1/3 * 1 = 1/3,如果不替换,则保持原来的。
以此类推,假如我们遇到到第i个节点的时候,此时如果替换的话,概率计算可以分为两步的概率,第一步是先在前i个节点中选择1个,概率是1/i,第二步是选择替换,也就是1/1,此时概率就是1/i.
所以我们可以在遇到第i个节点的时候,如果概率小于等于1/i,则替换就可以了。
数组存储链表参考实现1
水库算法参考实现2
实现1
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
* @param {ListNode} head
*/
// Runtime: 308 ms, faster than 5.54% of JavaScript online submissions for Linked List Random Node.
// Memory Usage: 50 MB, less than 5.21% of JavaScript online submissions for Linked List Random Node.
var Solution = function (head) {
let p = head;
this.nums = [];
while (p.next) {
this.nums.push(p.next.val);
p = p.next;
}
};
/**
* Returns a random node's value.
* @return {number}
*/
Solution.prototype.getRandom = function () {
const len = this.nums.length;
const i = Math.floor(Math.random() * (len + 1));
return this.nums[i];
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(head)
* var param_1 = obj.getRandom()
*/
实现2
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
* @param {ListNode} head
*/
// Runtime: 308 ms, faster than 5.54% of JavaScript online submissions for Linked List Random Node.
// Memory Usage: 50 MB, less than 5.21% of JavaScript online submissions for Linked List Random Node.
var Solution = function (head) {
// let p = head;
// this.nums = [];
// while (p.next) {
// this.nums.push(p.next.val);
// p = p.next;
// }
this.head = head;
};
/**
* Returns a random node's value.
* @return {number}
*/
// Runtime: 120 ms, faster than 65.35% of JavaScript online submissions for Linked List Random Node.
// Memory Usage: 46.3 MB, less than 19.25% of JavaScript online submissions for Linked List Random Node.
Solution.prototype.getRandom = function () {
// const len = this.nums.length;
// const i = Math.floor(Math.random() * (len + 1));
// return this.nums[i];
let p = this.head;
let res = p.val;
for (let i = 1; p != null; i++) {
p = p.next;
if (Math.random() <= 1 / (i + 1)) {
res = p != null ? p.val : res;
}
}
return res;
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(head)
* var param_1 = obj.getRandom()
*/
238. 除自身以外数组的乘积
题目描述
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
例子1
input: [1,2,3,4]
output: [24,12,8,6]
思考
1 题目比较简单,首先想到了使用一个前置数组保存i之前的结果,一个后置数组保持i之后的数组的乘积,然后相乘就可以了
2 如果想使用常数时间,可以看看实现1有哪些需要节省的,可以发现可以把前置数组用结果数组表示
参考实现1
参考实现2
实现1
/**
* @param {number[]} nums
* @return {number[]}
*/
// Runtime: 692 ms, faster than 13.05% of JavaScript online submissions for Product of Array Except Self.
// Memory Usage: 51.3 MB, less than 8.63% of JavaScript online submissions for Product of Array Except Self.
export default (nums) => {
const preNums = [1];
const afterNums = [1];
for (let i = 1; i < nums.length; i++) {
preNums[i] = preNums[i - 1] * nums[i - 1];
}
for (let j = nums.length - 2, k = 1; j >= 0; j--, k++) {
const temp = afterNums[afterNums.length - k] * nums[j + 1];
afterNums.unshift(temp);
}
// console.log(preNums, afterNums);
const res = [];
for (let i = 0; i < nums.length; i++) {
res[i] = preNums[i] * afterNums[i];
}
return res;
};
实现2
/**
* @param {number[]} nums
* @return {number[]}
*/
// Runtime: 112 ms, faster than 92.27% of JavaScript online submissions for Product of Array Except Self.
// Memory Usage: 49.7 MB, less than 54.84% of JavaScript online submissions for Product of Array Except Self.
export default (nums) => {
const len = nums.length;
const res = [];
res[0] = 1;
for (let i = 1; i < len; i++) {
res[i] = res[i - 1] * nums[i - 1];
}
let right = 1;
for (let i = len - 1; i >= 0; i--) {
res[i] *= right;
right *= nums[i];
}
return res;
};
169. 多数元素
题目描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
进阶:
尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题
例子1
input: [3,2,3]
output: 3
例子2
input: [2,2,1,1,1,2,2]
output: 2
思考
1 可以先排序,然后查找超过 ⌊ n/2 ⌋ 的元素
2 进阶是是使用Boyer-Moore Majority Vote算法,这个算法原理也很简单,就是发现数组中两个不同的元素直接删除,最后剩下的就是想要的结果。因为肯定存在多数元素,而多数元素的数量肯定大于 ⌊ n/2 ⌋ 的元素
参考实现1
参考实现2
实现1
/**
* @param {number[]} nums
* @return {number}
*/
// Runtime: 92 ms, faster than 31.11% of JavaScript online submissions for Majority Element.
// Memory Usage: 43.1 MB, less than 7.90% of JavaScript online submissions for Majority Element.
export default (nums) => {
if (nums.length === 1) return nums[0];
nums.sort((a, b) => a - b);
const max = Math.floor(nums.length / 2);
for (let i = 0; i < nums.length; i++) {
const tempMax = i + max;
if (nums[tempMax] === nums[i]) {
return nums[i];
}
if (tempMax > nums.length) {
break;
}
}
};
实现2
/**
* @param {number[]} nums
* @return {number}
*/
// Runtime: 68 ms, faster than 99.88% of JavaScript online submissions for Majority Element.
// Memory Usage: 40.8 MB, less than 74.90% of JavaScript online submissions for Majority Element.
export default (nums) => {
if (nums.length === 1) return nums[0];
let major = nums[0];
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (count === 0) {
count++;
major = nums[i];
} else if (major === nums[i]) {
count++;
} else {
count--;
}
}
return major;
};
470. 用 Rand7() 实现 Rand10()
题目描述
已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。
不要使用系统的 Math.random() 方法。
例子1
input: 1
output: [7]
例子2
input: 2
output: [8,4]
例子3
input: 3
output: [8,1,10]
提示
1 rand7 已定义。
2 传入参数: n 表示 rand10 的调用次数。
进阶
1 rand7()调用次数的 期望值 是多少 ?
2 你能否尽量少调用 rand7() ?
思考
1 这里使用随机数生成随机数,比较关键的是几点
1.1 首先是每个数出现的概率必须是随机的,比如rand10这里从1到10的概率必须都是1/10.
1.2
已知 rand_N() 可以等概率的生成[1, N]范围的随机数
那么:
(rand_X() - 1) × Y + rand_Y() ==> 可以等概率的生成[1, X * Y]范围的随机数
即实现了 rand_XY()
1.3 如果已经rand49,那么如何求rand10呢?
因为rand10是要求选择从1到10的概率都是1/10,现在已经知道rand49,也就是说使用rand49选择从1到49的概率都是1/49,那么rand10是什么呢?
可以想一下,假设选择2的概率是什么?
假如通过rand49函数执行一次就得到2,那么概率就是1/49
如果需要执行rand49函数两次得到2呢,那么概率就是第一次没有取到2,那么概率就是39/49,第二次取到2,那么概率是1/49,所以这次概率是39/49 * 1 / 49
同理第三次得到2,那么概率是 39/49 * 39/49 * 1 / 49
那么执行n次,总概率就是1/49 * ((1 * (1 - (39 / 49)^n)) / 1- (39/49) = 1 / 10
同理可以得到选择1到10的概率都是 1/10
到这里基本上就可以得到实现1
1.4 从1.3可以看到从rand49到rand10,所有大于10的都被舍弃了,那是不是有其他方法可以更大效率的利用这些从11到49的数呢?
这里有个规律是 要实现rand10(),就需要先实现rand_N(),并且保证N大于10且是10的倍数。这样再通过rand_N() % 10 + 1 就可以得到[1,10]范围的随机数了。
参考实现1
使用1.4 节省时间,可以参考实现2
参考: leetcode-cn.com/problems/im…
实现1
/**
* The rand7() API is already defined for you.
* var rand7 = function() {}
* @return {number} a random integer in the range 1 to 7
*/
// Runtime: 124 ms, faster than 29.03% of JavaScript online submissions for Implement Rand10() Using Rand7().
// Memory Usage: 49.4 MB, less than 6.45% of JavaScript online submissions for Implement Rand10() Using Rand7().
const rand10 = () => {
let a = rand7();
let b = rand7();
// 得到rand49
let num = (a - 1) * 7 + b;
if (num <= 10) return num;
return rand10();
};
export default rand10;
时间复杂度O(1)
实现2
/**
* The rand7() API is already defined for you.
* var rand7 = function() {}
* @return {number} a random integer in the range 1 to 7
*/
// Runtime: 112 ms, faster than 96.77% of JavaScript online submissions for Implement Rand10() Using Rand7().
// Memory Usage: 47.4 MB, less than 48.39% of JavaScript online submissions for Implement Rand10() Using Rand7().
const rand10 = () => {
let a = rand7();
let b = rand7();
// 得到rand49
let num = (a - 1) * 7 + b;
if (num <= 40) return (num % 10) + 1;
a = num - 40; // rand 9
b = rand7();
num = (a - 1) * 7 + b; // rand 63
if (num <= 60) return (num % 10) + 1;
a = num - 60; // rand 3
b = rand7();
num = (a - 1) * 7 + b; // rand 21
if (num <= 20) return (num % 10) + 1;
return rand10();
};
export default rand10;
202. 快乐数
题目描述
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
例子1
input: 19
output: true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
例子2
input: 2
output: false
思考
1 使用一个Set存储已经找到了,如果发现循环了,返回fasle
实现1
/**
* @param {number} n
* @return {boolean}
*/
// Runtime: 96 ms, faster than 35.42% of JavaScript online submissions for Happy Number.
// Memory Usage: 39.9 MB, less than 71.78% of JavaScript online submissions for Happy Number.
export default (n) => {
const inLoop = new Set();
let squareSum;
let remain;
while (!inLoop.has(n)) {
inLoop.add(n);
squareSum = 0;
while (n > 0) {
remain = n % 10;
squareSum += remain * remain;
n = Math.floor(n / 10);
}
if (squareSum === 1) {
return true;
} else {
n = squareSum;
}
}
return false;
};