一、数学推导
1、平方根
牛顿迭代法
class Solution {
public int mySqrt(int x) {
double guess = x;
double lastGuess;
do {
//牛顿-拉夫森迭代法
lastGuess = guess;
guess = (guess + x / guess) / 2;
} while (Math.abs(guess - lastGuess) > 0.1);
return (int)guess;
}
}
二、前缀和
1、和为k的子数组
-
题目
-
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数 。子数组是数组中元素的连续非空序列。
-
-
思路
- 使用前缀和的方法可以解决这个问题,因为我们需要找到和为k的连续子数组的个数。通过计算前缀和,我们可以将问题转化为求解两个前缀和之差等于k的情况。假设数组的前缀和数组为prefixSum,其中prefixSum[i]表示从数组起始位置到第i个位置的元素之和。那么对于任意的两个下标i和j(i < j),如果prefixSum[j] - prefixSum[i] = k,即从第i个位置到第j个位置的元素之和等于k,那么说明从第i+1个位置到第j个位置的连续子数组的和为k。通过遍历数组,计算每个位置的前缀和,并使用一个哈希表来存储每个前缀和出现的次数。在遍历的过程中,我们检查是否存在prefixSum[j] - k的前缀和,如果存在,说明从某个位置到当前位置的连续子数组的和为k,我们将对应的次数累加到结果中。这样,通过遍历一次数组,我们可以统计出和为k的连续子数组的个数,并且时间复杂度为O(n),其中n为数组的长度。
- 使用前缀和的方法可以解决这个问题,因为我们需要找到和为k的连续子数组的个数。通过计算前缀和,我们可以将问题转化为求解两个前缀和之差等于k的情况。假设数组的前缀和数组为prefixSum,其中prefixSum[i]表示从数组起始位置到第i个位置的元素之和。那么对于任意的两个下标i和j(i < j),如果prefixSum[j] - prefixSum[i] = k,即从第i个位置到第j个位置的元素之和等于k,那么说明从第i+1个位置到第j个位置的连续子数组的和为k。通过遍历数组,计算每个位置的前缀和,并使用一个哈希表来存储每个前缀和出现的次数。在遍历的过程中,我们检查是否存在prefixSum[j] - k的前缀和,如果存在,说明从某个位置到当前位置的连续子数组的和为k,我们将对应的次数累加到结果中。这样,通过遍历一次数组,我们可以统计出和为k的连续子数组的个数,并且时间复杂度为O(n),其中n为数组的长度。
-
题解
class Solution { public int subarraySum(int[] nums, int k) { // 前缀和 Map<Integer, Integer> map = new HashMap<>(); int sum = 0; int count = 0; map.put(0, 1); for (int num : nums) { sum += num; if (map.containsKey(sum - k)) { count += map.get(sum - k); } map.put(sum, map.getOrDefault(sum, 0) + 1); } return count; } }
2、路径总和III
- 题目
- 给定一个二叉树的根节点
root
,和一个整数targetSum
,求该二叉树里节点值之和等于targetSum
的 路径 的数目。
- 给定一个二叉树的根节点
- 思路:DFS/前缀和
- 题解
class Solution { int ans = 0; //用哈希表统计 <前缀和, 个数> Map<Long, Integer> map = new HashMap<>(); public int pathSum(TreeNode root, int targetSum) { map.put(0l, 1); dfs(root, 0l, targetSum); return ans; } public void dfs(TreeNode root,long sum, int targetSum) { if (root == null) { return; } //计算当前的前缀和 sum += root.val; //若存在前缀和sum - targetSum的路径 //sum - (sum - targetSum)的路径就是所求路径 ans += map.getOrDefault(sum - targetSum, 0); //将当前的前缀和加入map中 map.put(sum, map.getOrDefault(sum, 0) + 1); //递归调用left&right dfs(root.left, sum, targetSum); dfs(root.right, sum, targetSum); //遍历结束要移除当前的前缀和 map.put(sum, map.get(sum) - 1); } }
- 题目
三、滑动窗口
1、找到字符串中所有字母异位词
- 题目
- 给定两个字符串
s
和p
,找到s
中所有p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
- 给定两个字符串
- 思路
- 题解
class Solution { public List<Integer> findAnagrams(String s, String p) { // 空间复杂度O(26) // 时间复杂度O(m + 26 * (n - m)) List<Integer> list = new ArrayList<>(); int[] s1 = new int[26]; int[] p1 = new int[26]; // 建立两个数组存放字符串中字母出现的词频,并以此作为标准比较 // 当滑动窗口的首位在s[0]处时 (相当于放置滑动窗口进入数组) for (int i = 0; i < p.length(); i++) { s1[s.charAt(i) - 'a']++; p1[p.charAt(i) - 'a']++; } // 判断0放置处是否有异位词 (在放置时只需判断一次) if (Arrays.equals(s1, p1)) { list.add(0); } 开始让窗口进行滑动,i是滑动前的首位 for (int i = 0; i < s.length() - p.length(); i++) { // 将滑动前首位的词频删去 s1[s.charAt(i) - 'a']--; // 增加滑动后最后一位的词频(以此达到滑动的效果) s1[s.charAt(i + p.length()) - 'a']++; // 进行判断 if (Arrays.equals(s1, p1)) { list.add(i + 1); } } return list; } }