You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
5.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class24;
import java.util.LinkedList;
// 测试链接https://leetcode.com/problems/gas-station
public class Code03_GasStation {
// 这个方法的时间复杂度O(N)额外空间复杂度O(N)
public static int canCompleteCircuit(int[] gas, int[] cost) {
boolean[] good = goodArray(gas, cost);
for (int i = 0; i < gas.length; i++) {
if (good[i]) {
return i;
}
}
return -1;
}
public static boolean[] goodArray(int[] g, int[] c) {
int N = g.length;
int M = N << 1;
int[] arr = new int[M];
for (int i = 0; i < N; i++) {
arr[i] = g[i] - c[i];
arr[i + N] = g[i] - c[i];
}
for (int i = 1; i < M; i++) {
arr[i] += arr[i - 1];
}
// 举个例子说明一下
// 比如纯能数组(也就是燃料 - 距离之后)的数组 :
// 纯能数组 = 3, 2,-6, 2, 3,-4, 6
// 数组下标 = 0 1 2 3 4 5 6
// 客观上说:
// 0位置不是良好出发点
// 1位置不是良好出发点
// 2位置不是良好出发点
// 3位置是良好出发点
// 4位置不是良好出发点
// 5位置不是良好出发点
// 6位置是良好出发点
// 把数组增倍之后 :
// arr = 3, 2,-6, 2, 3,-4, 6, 3, 2,-6, 2, 3,-4, 6
// 然后计算前缀和 :
// arr = 3, 5,-1, 1, 4, 0, 6, 9,11, 5, 7,10, 6,12
// index = 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// 这些就是上面发生的过程
// 接下来生成长度为N的窗口
LinkedList<Integer> w = new LinkedList<>();
for (int i = 0; i < N; i++) {
while (!w.isEmpty() && arr[w.peekLast()] >= arr[i]) {
w.pollLast();
}
w.addLast(i);
}
// 上面的过程就是先遍历N个数然后建立窗口
// arr =[3, 5,-1, 1, 4, 0, 6],9,11, 5, 7,10, 6,12
// index = 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// w中的内容如下:
// index: 2 5 6
// value: -1 0 6
// 左边是头,右边是尾,从左到右严格变大
// 此时代表最原始的arr的这部分的数字:
// 原始的值 = [3, 2,-6, 2, 3,-4, 6],3, 2,-6, 2, 3,-4, 6
// 原始下标 = 0 1 2 3 4 5 6 0 1 2 3 4 5 6
// 上面这个窗口中累加和最薄弱的点就是w中最左信息
// 也就是会累加出,-1这个值所以会走不下去。
// 宣告了此时0位置不是良好出发点。
// 接下来的代码,就是依次考察每个点是不是良好出发点。
// 目前的信息是:
// 计算的前缀和 :
// arr =[3, 5,-1, 1, 4, 0, 6],9,11, 5, 7,10, 6,12
// index = 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// w中的内容如下:
// index: 2 5 6
// value: -1 0 6
// 此时代表最原始的arr的这部分的数字:
// 原始的值 = [3, 2,-6, 2, 3,-4, 6],3, 2,-6, 2, 3,-4, 6
// 原始下标 = 0 1 2 3 4 5 6 0 1 2 3 4 5 6
// 现在让窗口往下移动
// 计算的前缀和 :
// arr = 3,[5,-1, 1, 4, 0, 6, 9],11, 5, 7,10, 6,12
// index = 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// w中的内容如下:
// index: 2 5 6 7
// value: -1 0 6 9
// 此时代表最原始的arr的这部分的数字:
// 原始的值 = 3,[2,-6, 2, 3,-4, 6, 3],2,-6, 2, 3,-4, 6
// 原始下标 = 0 1 2 3 4 5 6 0 1 2 3 4 5 6
// 上面这个窗口中累加和最薄弱的点就是w中最左信息
// 但是w最左的值是-1啊而这个窗口中最薄弱的累加和是-4啊。
// 对!所以最薄弱信息 = 窗口中的最左信息 - 窗口左侧刚出去的数(代码中的offset!)
// 所以,最薄弱信息 = -1 - 0位置的3(窗口左侧刚出去的数) = -4
// 看到了吗?最薄弱信息,依靠这种方式,加工出来了!
// 宣告了此时1位置不是良好出发点。
// 我们继续,让窗口往下移动
// 计算的前缀和 :
// arr = 3, 5,[-1, 1, 4, 0, 6, 9,11], 5, 7,10, 6,12
// index = 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// w中的内容如下:
// index: 2 5 6 7 8
// value: -1 0 6 9 11
// 此时代表最原始的arr的这部分的数字:
// 原始的值 = 3, 2,[-6, 2, 3,-4, 6, 3, 2],-6, 2, 3,-4, 6
// 原始下标 = 0 1 2 3 4 5 6 0 1 2 3 4 5 6
// 上面这个窗口中累加和最薄弱的点就是w中最左信息
// 但是w最左的值是-1啊而这个窗口中最薄弱的累加和是-6啊。
// 对!所以最薄弱信息 = 窗口中的最左信息 - 窗口左侧刚出去的数(代码中的offset!)
// 所以,最薄弱信息 = -1 - 1位置的5(窗口左侧刚出去的数) = -6
// 看到了吗?最薄弱信息,依靠这种方式,加工出来了!
// 宣告了此时2位置不是良好出发点。
// 我们继续,让窗口往下移动
// 计算的前缀和 :
// arr = 3, 5, -1,[1, 4, 0, 6, 9,11, 5], 7,10, 6,12
// index = 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// w中的内容如下:
// index: 5 9
// value: 0 5
// 没错9位置的5进来让6、7、8位置从w的尾部弹出了
// 同时原来在w中的2位置已经过期了所以也弹出了因为窗口左边界已经划过2位置了
// 此时代表最原始的arr的这部分的数字:
// 原始的值 = 3, 2, -6,[2, 3,-4, 6, 3, 2, -6],2, 3,-4, 6
// 原始下标 = 0 1 2 3 4 5 6 0 1 2 3 4 5 6
// 上面这个窗口中累加和最薄弱的点就是w中最左信息
// 但是w最左的值是0啊而这个窗口中最薄弱的累加和是1啊
// 对!所以最薄弱信息 = 窗口中的最左信息 - 窗口左侧刚出去的数(代码中的offset!)
// 所以,最薄弱信息 = 0 - 2位置的-1(窗口左侧刚出去的数) = 1
// 看到了吗?最薄弱信息,依靠这种方式,加工出来了!
// 宣告了此时3位置是良好出发点。
// 往下同理
boolean[] ans = new boolean[N];
for (int offset = 0, i = 0, j = N; j < M; offset = arr[i++], j++) {
if (arr[w.peekFirst()] - offset >= 0) {
ans[i] = true;
}
if (w.peekFirst() == i) {
w.pollFirst();
}
while (!w.isEmpty() && arr[w.peekLast()] >= arr[j]) {
w.pollLast();
}
w.addLast(j);
}
return ans;
}
}