1. 二分法
O (logN)
374. 猜数字大小
猜数字游戏的规则如下:
- 每轮游戏,我都会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。
- 如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。
你可以通过调用一个预先定义好的接口 int guess(int num) 来获取猜测结果,返回值一共有 3 种可能的情况(-1,1 或 0):
- -1:我选出的数字比你猜的数字小
pick < num - 1:我选出的数字比你猜的数字大
pick > num - 0:我选出的数字和你猜的数字一样。恭喜!你猜对了!
pick == num
返回我选出的数字。
/**
* Forward declaration of guess API.
* @param {number} num your guess
* @return -1 if num is lower than the guess number
* 1 if num is higher than the guess number
* otherwise return 0
* var guess = function(num) {}
*/
/**
* @param {number} n
* @return {number}
*/
var guessNumber = function(n) {
let start = 0
let end = n
while (start < end) {
let mid =start + Math.floor( (end - start ) / 2) //Math.floor去除多余小数入 1.5 => 1
let guessNum = guess(mid)
// console.log("start",start,"end",end,"mid",mid,"guessNum",guessNum)
//如果返回 -1 或者 1 证明 最小值和最大值都不在范围内。
if(guessNum == -1) { // 实际的值比较小
end = mid -1
} else if(guessNum == 1) {// 实际的值比较大
start = mid + 1 //每次都要+1,因为mid 确定不在start 需要+1
} else {
return mid
}
}
return start
};
// @lc code=end
69. Sqrt(x)
- 给你一个非负整数
x,计算并返回x的 算术平方根 。 - 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
- 注意: 不允许使用任何内置指数函数和算符,例如
pow(x, 0.5)或者x ** 0.5。
示例 1:
输入: x = 4
输出: 2
示例 2:
输入: x = 8
输出: 2
解释: 8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
答题
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function(x) {
//实现逻辑,从1 到 x/2 之间 一直找mid,并且判断 mid *mid 是否等于 x
if (x < 2) {
return x
}
let left = 1
let right = Math.floor(x / 2)
let mid
while (left <= right) {
mid = Math.floor( left + (right - left ) / 2 )
if(mid * mid == x) {
return mid
}else {
if(mid * mid > x) {
right = mid - 1
}else {
left = mid + 1
}
}
}
return right
};
50. Pow(x, n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn )。
示例 1:
输入: x = 2.00000, n = 10
输出: 1024.00000
示例 2:
输入: x = 2.10000, n = 3
输出: 9.26100
示例 3:
输入: x = 2.00000, n = -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
答题
/**
* @param {number} x
* @param {number} n
* @return {number}
*/
//for + parseFloat 迭代
var myPow = function(x, n) {
if (n == 0) {
return 1;
}
x = parseFloat(x);
if (n < 0) {
x = parseFloat(1 / x);
n = -n;
}
var subResult = x;
var result = 1;
for (var i=n; i>0; i=parseInt(i/2)) {
if (i&1 == 1) {
result *= subResult;
}
subResult *= subResult;
}
return result;
};
//for + 迭代
var myPow = function (x, n) {
// 0和1的n次幂都为其本身
if (x === 0 || x === 1) {
return x;
}
// 负1的偶数幂为1,奇数幂为-1
if (x === -1) {
return n % 2 ? -1 : 1;
}
// 特殊处理会超时的Case
// 或者可以写成n === -0x80000000
if (n === -2147483648) {
return 0;
}
let result = 1; // 初始值为1
const len = Math.abs(n); // 需要计算的次数,n有可能为负,因此要取绝对值
for (let i = 0; i < len; i++) {
result =
n > 0
? result * x // n>0时即乘n次
: result / x; // n>0时即/除n次
}
return result;
};
//分治算法
var myPow = function(x, n) {
if (n === 0) return 1 // n=0直接返回1
if (n < 0) { //n<0时 x的n次方等于1除以x的-n次方分
return 1 / myPow(x, -n)
}
if (n % 2) { //n是奇数时 x的n次方 = x*x的n-1次方
return x * myPow(x, n - 1)
}
return myPow(x * x, n / 2) //n是偶数,使用分治,一分为二,等于x*x的n/2次方
};
//系统api
var myPow = function(x, n) {
return Math.pow(x,n)
};
//系统语法
var myPow = function(x, n) {
return x ** n
};
实现js里面的leftpad方法
leftpad(str,len,ch) ,是左边自动补齐剩下的长度,以ch插入,
如: leftpad("jason",10,'b') 结果为 bbbbbjason
问题分析
无论是否使用ch,ch都在一直的指数递增
通过len%2 == 1 判断,可以灵活控制total的需要 累加的ch 内容
累加关系
1-4 : b , bb , b + bb , bbbb ,
5-8: b + bbbb , bb + bbbb , b + bb + bbbb , bbbbbbbb
核心逻辑:
- 当遇到可以被2整除的多次的数,如4,8,16 ,就等价于 一直ch += ch ,直到判断1%2 == 1,total 直接加上背倍数级的b
- 当遇到可以被2整除的1次的数,如6,10,14,就等价于 ch += ch,只加一次,然后 total = total + ch(bb)
- 当遇到不可以被2整除的1次的数,如3,5,7,total直接等于 ch , total = ch(b)
答题
//二分法 + 位运算优化 求余和除法
function leftpad(str,len,ch) {
if(len <= str.length) {
return str
}
len = len - str.length
let total = ''
// console.log("len:",len)
while (true) {
if (len & 1) { //求余 相当于 做和1的位与运算
total += ch
// console.log("total += ch:",total)
}
if (len == 1) {//最后 只剩下 1 就直接+str 返回最终结果
return total + str
}
ch += ch //这里不断翻倍的一直累加
// console.log("ch += ch:",ch)
len = len >> 1 // 右移相当于 除于2
// console.log("parseInt(len/2):",len)
}
}
//二分法
function leftpad(str,len,ch) {
if(len <= str.length) {
return str
}
len = len - str.length
let total = ''
// console.log("len:",len)
while (true) {
if (len%2 == 1) { //判断当前len 或折半后的len 是否有余数,有的话 把当前二分的数据都保存一下
total += ch
// console.log("total += ch:",total)
}
if (len == 1) {//最后 只剩下 1 就直接+str 返回最终结果
////因为怎么折半后 最终都有 parseInt(3/2) = 1 ,或者2/2=1
return total + str
}
ch += ch //这里不断翻倍的一直累加
// console.log("ch += ch:",ch)
len = parseInt(len/2) //这里开始折半
// console.log("parseInt(len/2):",len)
}
}
//测试验证逻辑
leftpad('jason',15,'b')
ch = 'b'
total = ''
len = 15-5 = 10 开始
//注意ch 一直都在翻倍的增加 b bb bbbb bbbbbbbb
10 % 2 余0. total = '' ch = bb. len = 10 / 2 = 5
5 % 2 余1. total = 'bb' , ch = bbbb. len = 5 / 2 = 2
2 % 2 余0. total = 'bb' , ch = bbbbbbbb. len = 2 / 2 = 1
1 % 2 余1. total = 'bbbbbbbbbb' , return total + str
//Array.join
function leftpad(str,len,ch) {
if(len <= str.length) {
return str
}
len = len-str.length
return Array(len+1).join(ch)+str //array join会少一位
}
//for循环
function leftpad (str,len,ch) {
if(len <= str.length) {
return str
}
if (!ch) ch = ' '; //这里修正当输入 为 ''空字符串时候 改为 ' '
let preStr = ""
len = len-str.length
console.log("len",len)
for (let i = 0;i < len ; i++) {
preStr = preStr + ch
}
return preStr + str
}
//while循环
function leftpad (str,len,ch) {
if(len <= str.length) {
return str
}
if (!ch) ch = ' '; //这里修正当输入 为 ''空字符串时候 改为 ' '
len = len-str.length
while (len > 0) {
str = ch + str
len--
}
return str
}
// console.log(leftpad("jason",10,"a"))
//console.log(leftpad2("jason",10,'b'))