1.验证ip地址
题目:
编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址。
如果是有效的 IPv4 地址,返回 "IPv4" ;
如果是有效的 IPv6 地址,返回 "IPv6" ;
如果不是上述类型的 IP 地址,返回 "Neither" 。
IPv4 地址由十进制数和点来表示,每个地址包含 4 个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。
IPv6 地址由 8 组 16 进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。
然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (::) 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。
同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。
思路:split分割,然后手动遍历或者用正则都行。正则比较快
时间复杂度O(n),空间复杂度O(n)
/**
* @param {string} IP
* @return {string}
*/
var validIPAddress = function(IP) {
const arrIp4 = IP.split('.');
const arrIp6 = IP.split(":");
const regIP4 = /^0$|^([1-9]\d{0,2})$/;
const regIP6 = /^[0-9a-fA-F]{1,4}$/;
if(arrIp4.length === 4) {
if(arrIp4.every((item) => regIP4.test(item) && item < 256)) {
return "IPv4";
}
} else if(arrIp6.length === 8) {
if(arrIp6.every((item) => regIP6.test(item))) {
return "IPv6";
}
}
return "Neither";
};
2.用Rand7()实现Rand10()
思路:拒绝采样或者分布计算都可以。
拒绝采样的意思,是先计算得到随机数,判断是否大于5,大于的话重新计算。然后,我们就均匀的得到了1-5的随机数。再取一次随机数,这次数字不能等于1或者7,然后我们得到了一个范围6的随机数,判断这个数在左区间还是右区间(比如舍弃7,左右区间就是1-3和2-6),左区间+0,右区间+5,这样就得到了一个1-10的随机数。(相当于随机了两次,一次随机是获取1-5的数,一次随机是随机+5)
/**
* The rand7() API is already defined for you.
* var rand7 = function() {}
* @return {number} a random integer in the range 1 to 7
*/
var rand10 = function() {
let result = rand7();
while (result > 5) result = rand7();
let temp = rand7();
while (temp === 7) temp = rand7();
return temp <= 3 ? result : result + 5;
};
3.火柴拼正方形
题目:
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
思路:先排除不可能的情况,比如总长不能被4整除。然后,对数组进行遍历,先声明一个set,记录当前数字可能组成的边长组合,在遍历数组的时候更新set,这样就能知道哪些长度是可以拼接起来的,然后判断1倍边长,2倍边长,3倍边长是否在set里存在,存在就说明正方形可以得到
时间复杂度O(nlogn),空间复杂度O(nlogn)
/**
* @param {number[]} nums
* @return {boolean}
*/
var makesquare = function(nums) {
const l = nums.length;
if (l < 4) return false;
const sum = nums.reduce((a, b) => a + b);
if (sum % 4) return false;
nums.sort((a, b) => a - b);
let curSum = 0;
const num = sum / 4;
if (num < nums[l - 1]) return false;
if (nums[l - 1] < num && nums[0] + nums[l - 1] > num) return false;
let set = new Set();
for (const item of nums) {
const temp = new Set();
temp.add(item);
for (const v of set) {
temp.add(v + item);
temp.add(v);
}
set = temp;
}
return set.has(num) && set.has(2 * num) && set.has(3 * num);
};
4.一和零
思路:
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
思路:背包问题。可以建立m*n的二维数组,dp[i,j]就是i个0和j个1时容纳的最大子集的数量,有:
dp[i][j]=Math.max(dp[i,j],dp[m-i][n-j] + 1)
遍历数组时不断更新dp[i,j]的值
时间复杂度O(mnl),空间复杂度O(mn)
/**
* @param {string[]} strs
* @param {number} m
* @param {number} n
* @return {number}
*/
var findMaxForm = function(strs, m, n) {
const l = strs.length;
const valueList = strs.map((item) => {
let v0 = 0,
v1 = 0;
for (let i = 0; i < item.length; i++) {
if (item[i] == "0") {
v0++;
} else {
v1++;
}
}
return [v0, v1];
});
const dp = new Array(m + 1).fill("").map(() => new Array(n + 1).fill(0));
for (let i = 0; i < l; i++) {
const [v0, v1] = valueList[i];
for (let j = v0; j <= m; j++) {
for (let k = v1; k <= n; k++) {
dp[j][k] = Math.max(dp[j][k], dp[m - j][n - k] + 1);
}
}
}
return dp[m][n];
};
5.汉明距离总和
题目:
两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。
计算一个数组中,任意两个数之间汉明距离的总和。
思路:因为数组成员转成二进制之后,长度不一样,所以先对二进制字符串前缀补0,补全到32位长度,(最大成员的值小于这个值即可)
然后,可以记录每一位的0和1的数量,那么这一位的汉明距离就是两者的乘积。最后累加
时间O(n),空间复杂度O(1)
/**
* @param {number[]} nums
* @return {number}
*/
var totalHammingDistance = function(nums) {
const l = nums.length;
const v0 = new Array(32).fill(0);
const v1 = new Array(32).fill(0);
for (let i = 0; i < l; i++) {
const v = nums[i].toString(2).padStart(32, "0");
for (let i = 0; i < 32; i++) {
if (v[i] == "1") {
v1[i]++;
} else {
v0[i]++;
}
}
}
let res = 0;
for (let i = 0; i < 32; i++) {
res += v0[i] * v1[i];
}
return res;
};