前缀和(本质:空间换时间)
1. 数组种的双指针
借助一个变量做到了降维的优化
1.1 左右指针
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length - 1;
int max = 0;
while (left <= right){
int area = (right - left) * Math.min(height[left], height[right]);
max = Math.max(max, area);
if (height[left] <= height[right]){ // 说明 height[left] 与右边任意线段都无法组成一个比 ans 更大的面积【因为高已确定,就是height[left]】
left++;
}else {
right--;
}
}
return max;
}
}
1.2 快慢指针
1.3 2个数组,用 2 个指针分别遍历
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int start1 = 0;
int start2 = 0;
int index = 0;
int[] arr = Arrays.copyOf(nums1, m);
while (start1 < m && start2 < n){
int A = arr[start1];
int B = nums2[start2];
if (A <= B){
start1++;
}else {
start2++;
}
nums1[index++] = Math.min(A, B);
}
if (start1 == m){
for (int i = start2; i < nums2.length; i++) {
nums1[index++] = nums2[i];
}
}
if (start2 == n){
for (int i = start1; i < arr.length; i++) {
nums1[index++] = arr[i];
}
}
}
}
2. 前缀和
1204. 统计【优美子数组】:k > 1 (默认
cclass Solution {
public int numberOfSubarrays(int[] nums, int k) {
// 通过 % 2 将找 k 个奇数的子数组 ===> 找和为k 的子数组 ==> s[i] - s[j] = k ===> 2数之和
// [1 1 2 1 1]
// 1 1 0 1 1
// s[i]:nums[0] - nums[i] 和 ===> 非递减数列
// 即:0 1 2 2 3 4 【注意前面要补个 0】
// 两数之和
int[] s = new int[nums.length + 1];
for (int i = 1; i < s.length; i++) {
s[i] = s[i - 1] + nums[i - 1] % 2;
}
Map<Integer, Integer> map = new HashMap<>();
int count = 0;
for (int i = 1; i < s.length; i++) {
if (s[i] == k){
count++;
}
count += map.getOrDefault(s[i] - k, 0);
map.put(s[i], map.getOrDefault(s[i], 0) + 1); // 统计前缀和出现次数
}
return count;
}
}
560. 和为 K 的数组
class Solution {
public int subarraySum(int[] nums, int k) {
int[] s = new int[nums.length + 1];
for (int i = 1; i < s.length; i++) {
s[i] = s[i - 1] + nums[i - 1];
}
// 统计前缀和每个出现的次数
Map<Integer, Integer> map = new HashMap<>();
int count = 0;
for (int i = 1; i < s.length; i++) {
if (s[i] == k){ // 当前符合
count++;
}
count += map.getOrDefault(s[i] - k, 0); // 前方符合
map.put(s[i], map.getOrDefault(s[i], 0) + 1); // 逐步放入 map 中
}
return count;
}
}
OD223:数组连续和【res定义为:long 类型】【>= 某个值】
本质:前缀和数组是非递减数组
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(" ");
int target = Integer.parseInt(split[1]);
String[] temp = in.nextLine().split(" ");
int[] arr = new int[temp.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(temp[i]);
}
long[] sum = new long[temp.length + 1];
long count = 0;
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
if (sum[i] < target){
continue;
}
if (sum[i] >= target){ // 这个判断可以省略,因为前面已经有 continue了,走到这里的一定是 >= 的
count++;
}
// 临界值:sum[i] - target
int cur = i - 1;
while (cur >= 1 && sum[cur] > sum[i] - target){
cur--;
}
count += cur;
}
System.out.println(count);
}
}
OD168. 字符串比较【<= 某个值】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String A = in.nextLine();
String B = in.nextLine();
int target = Integer.parseInt(in.nextLine());
int[] arr = new int[A.length()];
for (int i = 0; i < arr.length; i++) {
arr[i] = Math.abs(A.charAt(i) - B.charAt(i));
}
int[] sum = new int[arr.length + 1];
int res = 0;
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
if (sum[i] <= target){ // 当前满足,那么减去任意一个数都符合【前缀和是非递减数组】`
res = Math.max(res, i);
continue;
}
int cur = i - 1;
while (cur >= 1 && sum[cur] >= sum[i] - target){
cur--;
}
res = Math.max(res, i - cur - 1); // 当前值不算!!!【因为:sum[i] > target】
}
System.out.println(res);
}
}
OD282. 查找接口成功率最优实践段
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int minAverageLost = Integer.parseInt(in.nextLine());
String[] split = in.nextLine().split(" ");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
List<Time> list = new ArrayList<>();
int[] sum = new int[arr.length + 1];
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
int index = 0;
while (index < i){
int temp = (int) Math.ceil((sum[i] - sum[index]) / (double)(i - index));
if (temp <= minAverageLost){
list.add(new Time(index, i - 1)); // 不包括开头!!1
break;
}
index++;
}
}
Collections.sort(list, new Comparator<Time>() {
@Override
public int compare(Time o1, Time o2) {
int len1 = o1.right - o1.left;
int len2 = o2.right - o2.left;
if (len1 != len2){
return len2 - len1;
}
return o1.left - o2.left;
}
});
if (list.size() == 0){
System.out.println("NULL");
return;
}
int len = list.get(0).right - list.get(0).left;
StringBuilder res = new StringBuilder();
for (Time time : list) {
if (time.right - time.left == len){
res.append(time.left + "-" + time.right + " ");
}
}
System.out.println(res.toString().trim());
}
}
class Time{
int left;
int right;
public Time(int left, int right) {
this.left = left;
this.right = right;
}
}
OD189. 分割数组的最大差值
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int count = Integer.parseInt(in.nextLine());
long[] arr = new long[count];
long total = 0;
for (int i = 0; i < count; i++) {
arr[i] = in.nextLong(); // 注意类型是 Long,要用 in.nextLong();
total += arr[i];
}
long[] sum = new long[count + 1];
long res = Long.MIN_VALUE;
for (int i = 1; i < count; i++) { // i = count - 1 时代表除去最后一位的和
sum[i] = sum[i - 1] + arr[i - 1];
res = Math.max(res, Math.abs(2 * sum[i] - total)); // 左 - 右
}
System.out.println(res);
}
}
OD130 最长连续子序列
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(",");
int target = Integer.parseInt(in.nextLine());
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
int[] sum = new int[arr.length + 1];
Map<Integer, Integer> map = new HashMap<>();
int res = -1;
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i - 1] + arr[i - 1];
if (sum[i] == target){
res = Math.max(res, i); // 当前符合,这个数之前都符合
}
if (map.containsKey(sum[i] - target)){
res = Math.max(res, i - map.get(sum[i] - target));
}
map.putIfAbsent(sum[i], i); // 首次出现的位置
}
System.out.println(res);
}
}
241. 用连续自然数之和来表达整数
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int num = in.nextInt();
int[] sum = new int[num + 1];
Map<Integer, Integer> map = new HashMap<>();
List<Result> res = new ArrayList<>();
for (int i = 1; i < sum.length - 1; i++) {
int now = i - 1;
sum[i] = sum[i - 1] + now;
if (map.containsKey(sum[i] - num)){
res.add(new Result(map.get(sum[i] - num), i));
}
map.put(sum[i], i);
}
Collections.sort(res, (o1, o2) -> {
int len1 = o1.end - o1.start;
int len2 = o2.end - o2.start;
return len1 - len2;
});
System.out.println(num+"="+num); // 默认肯定有本身【所以最后的 size 也要 + 1】
res.stream().forEach(result -> {
StringBuilder sb = new StringBuilder();
for (int i = result.start; i < result.end; i++) {
sb.append(i + "+");
}
System.out.println(num+"=" + sb.toString().substring(0, sb.length() - 1));
});
System.out.println("Result:" + (res.size() + 1));
}
}
class Result{
int start;
int end;
public Result(int start, int end) {
this.start = start;
this.end = end;
}
}
3. 两数之和
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])){ // map 中有无符合的数
res[0] = i;
res[1] = map.get(target - nums[i]);
break;
}
map.put(nums[i], i);
}
return res;
}
}
4. 三数之和
关于 left、right 一起缩进原因:因为第一个数是定死的,后面个数是捆绑的,如果存在一个数,只能由另外一个数才能凑成 0,所以要消除就必须一起消除
class Solution {
List<Integer> list = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<Integer> temp = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (nums[i] > 0){ // 如果第一个数都大于 0了,后面就没有找的必要了
break;
}
if (i > 0 && nums[i] == nums[i - 1]){ # 1. i 去重
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right){
Integer A = nums[i];
Integer B = nums[left];
Integer C = nums[right];
int sum = A + B + C;
if (sum > 0){
right--;
}else if (sum < 0){
left++;
}else {
List<Integer> listTemp = new ArrayList<>();
listTemp.add(A);
listTemp.add(B);
listTemp.add(C);
res.add(listTemp);
while (right > left && nums[right - 1] == nums[right]){ // 2. right 去重
right--;
}
while (left < right && nums[left + 1] == nums[left]){ // 3. left 去重
left++;
}
left++;
right--;
}
}
}
return res;
}
}