50. Pow(x, n)
解题思路:
-
1. 绝对值 + 快速幂 + 迭代 ,由于题目 n 可能是 系统最小值 ,因此使用 n 的 绝对值 。
-
如果 n 是 系统最小值 ,先让 系统最小值 +1 (最后再乘以一个 x ),这是因为 对系统最小值求绝对值还是它自身 ,但是 +1 可以在正常范围内。
-
记 n 的绝对值为 N , 结果 res 初始为 1 , 只要 N 不为 0 ,就一直循环处理,每次将 x 倍乘自己,并将 N 右移 1 位, 只有 N 最低位是 1 时才把 x 累乘到结果 res 中。
-
循环结束后,判断一下原来的 n 如果是 系统最小 ,就将 res 再乘以 1 个 x ,以弥补因为之前 +1 少的一个 x 。
-
特判: n== 0 返回 1 (任意数的 0 次方都是 1 ), n==1 或 x==0 或 x==1 都返回 x ( x 的 1 次方还是 x , 0 或 1 这两个数的 n 次方还是自身)。
-
注意:最后返回的时候,需要判断如果 n 是 负数 ,则需要返回结果的 倒数 ,即 1 / res 。
解题思路:
-
2. 快速幂 + 迭代 ,可以首先使用 long 型变量 N 来接住 n ,这样就不用担心 n 可能是 整型的最小值 了,
-
然后对 N 取绝对值,依然是每次循环中将 x 倍乘,将 N 右移 1 位,只有 N 最低位是 1 时才把 x 乘到结果 res 中。
-
循环结束后也不需要另外多乘了,只需要判断 n 是不是负数,是的话返回结果的倒数即可。
解题思路:
-
3. 快速幂 + 折半递归 ,还是先使用 long 接收 n ,记为 N ,然后判断 N> 0 就调用递归 dfs(x, N) , N< 0 就返回 1 / dfs(x, -N) ,
-
dfs 函数中接收 long 型的 N ,每次将 N 折半进行递归调用 dfs(x, N/2) ,返回结果记为 y , 然后判断如果 N 是 偶数 就返回 y*y ,如果 N 是 奇数 就返回 y*y*x 。
-
递归终止:N== 0 时返回 1 , N==1 时返回 x 。
题思路:
-
4. 快速幂 + 折半递归 ,可以直接对 n 进行折半递归调用,返回结果记为 y , 如果 n 是偶数,返回 y*y , 如果 n 是奇数且 n>0 , 返回 y*y*x ,如果 n 是奇数且 n<0 , 返回 y*y*(1/x) 。
-
递归终止: n==0 时返回 1 ,n == 1时返回 x , n == -1时返回 1/x 。
注意:当n小于0时,res需要变分母,如2的-2次方 = 1 / 2的2次方。
69. x 的平方根
解题思路:
-
1. 二分查找 , x 的平方根肯定是 [0, x] 范围内的某个数,它乘以自身得到 x , 因为大于 x 的数乘以自身结果肯定超过 x 。
-
因此直接在 [0, x] 上进行 二分查找 即可,让 L 和 R 分别指向 0 和 x , 每次计算出中值 mid ,
-
然后判断如果 mid*mid ≤ x , 记录此时的 mid 作为候选答案,然后收缩 L 到右半边 [mid + 1, R] 上搜索,否则如果 mid*mid > x ,就收缩 R 到左半边 [L, mid - 1] 上搜索。
-
注意:在计算 mid*mid 时可能出现 整型越界 ,因此可以将所有变量使用 long 型来接受。最后返回结果时再转成 int 型。 也可以不用 long ,只需要将 mid * mid ≤ x 的判断改成 mid ≤ x / mid ,并增加特判 x == 0 或 x == 1 时直接返回 x ( 0 或 1 的平方根是自身,x为这两个值时计算出的mid是0,除数不能为0 )
解题思路:
-
2. 牛顿迭代法 ,先使用 long 型变量 a 接收 x ,然后循环判断只要 a*a > x ,就不断的将 a 更新为 (a + x / a)/ 2 , 最后返回 a 即可。(不要问我为什么这样做,问牛顿)
注意:
本题是输出整数部分,但是如果有些大厂要求结果要输出带小数的,可以使用
double
来接收
x
, 最后看要求需要保留几位小数将
a
转换一下即可。
java浮点型输出2位小数:
-
1) 可以使用 String.format("%.2f", x)
-
2) 可以使用 new DecimalFormat(".00").format(x)
-
3) 可以先 乘以 100 转 整型 再 除以 100.0f
263. 丑数
解题思路:
-
1. 数学模拟 , 根据丑数的定义,首先排除 0 和 负数 。
-
当 n > 0 时,若 n 是丑数,则 n 可以写成 n = 2^a * 3^b * 5^c 的形式, 而当 a,b,c 都是 0 时, n=1 。
-
因此可以对 n 反复除以 2,3,5 直到 n 不再包含质因数 2,3,5 为止,若最终剩下的数等于 1 ,则说明 n 不包含其他质因数,是丑数;否则不是丑数。
-
具体地,遍历 [2,3,5] 中的 每个质因数记为 factor , 循环判断如果 n 能被 factor 的 整除 ,就不停的让 n = n / factor 继续循环判断,直到 n 不能被当前的质因数整除为止。
-
最后所有质因数都处理完了,返回 n 是否等于 1 即可。
解题思路:
-
2. DFS , 3叉树前序遍历,从 n 开始,每次递归函数中访问当前根节点的三个子节点 [2,3,5] ,尝试用 n 除以每一个子节点,如果能除尽(没有余数),则将 [n / 当前值] 作为下一层的根节点进行DFS调用。
-
递归终止:当 n==1 ,即遇到叶子节点时,返回 true , 说明这一条DFS支路上全部可以被 [2,3,5] 除尽 。
-
递归调用中 有一个子节点返回 true ,父节点就返回 true 。 否则 最终就返回 false 。
-
特判:主函数判断 n<=0 时直接返回 false ,不用进行DFS,因为丑数是正整数。
264. 丑数 II
解题思路:
-
1. 小根堆 ,要得到从小到大的第 n 个丑数,也就是求 第 n 小,可以使用 小根堆 实现。
-
初始时堆为空。首先将最小的丑数 1 加入堆。每次弹出 堆顶 元素 x 就是堆中 最小的丑数 ,由于 2x, 3x, 5x 也是丑数,因此将 2x, 3x, 5x 加入堆。
-
上述做法会导致堆中出现 重复元素 的情况。为了避免重复元素,可以使用哈希 Set 去重,避免相同元素多次加入堆。
-
在排除重复元素的情况下,第 n 次从最小堆中取出的元素即为第 n 个丑数。
-
注意:每次取出堆顶再分别乘以 2、3、5 的时候,可能出现 整型溢出 ,为避免溢出问题, PriorityQueue 和 HashSet 中可都使用 long 型存储。
整个过程实际上是一个 BFS 的过程,每取出一个节点就将该节点所属的下一层的节点放入优先级队列中:
小根堆内部变化情况:
解题思路:
-
2. 动态规划 ,有点类似合并三个有序数组,但是在同一个 dp 数组上进行比较的,而不是显示的三个数组,
-
首先 dp[1] 初始为最小的丑数 1 ,然后设 3 个指针 i j k 分别指向下标 1 位置,接下来遍历 [2, n] 开始求 dp[2] 到 dp[n] 的值,
-
每次从 dp[i] * 2, dp[j] * 3, dp[k] * 5 三组数中选一个最小的丑数保存在 dp 中,并且 较小的那一组的指针下标向后移动一位 ,如果有 多组 都是最小值,则同时 向后移动多个指针 。
-
最后返回 dp[n] 的值。
下面看一下这个算法的过程:
注意这时,第五轮之后,i 和 j 的下标会同时向后移动: