加油站。在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。说明: 如果题目有解,该答案即为唯一答案。输入数组均为非空数组,且长度相同。输入数组中的元素均为非负数。力扣134。
纯能数组。gas[i]-distance[i]。
时间复杂度:O(N)。
额外空间复杂度:O(1)。
代码用golang编写。代码如下:
package main
import "fmt"
func main() {
gas := []int{1, 2, 3, 4, 5}
cost := []int{3, 4, 5, 1, 2}
ret := canCompleteCircuit(gas, cost)
fmt.Println(ret)
}
func canCompleteCircuit(gas []int, cost []int) int {
if len(gas) == 0 {
return -1
}
if len(gas) == 1 {
return twoSelectOne(gas[0] < cost[0], -1, 0)
}
good := stations(cost, gas)
for i := 0; i < len(gas); i++ {
if good[i] {
return i
}
}
return -1
}
func twoSelectOne(c bool, a int, b int) int {
if c {
return a
} else {
return b
}
}
func stations(cost []int, gas []int) []bool {
if len(cost) < 2 || len(cost) != len(gas) {
return nil
}
init0 := changeDisArrayGetInit(cost, gas)
if init0 == -1 {
return make([]bool, len(cost))
} else {
return enlargeArea(cost, init0)
}
}
func changeDisArrayGetInit(dis []int, oil []int) int {
init0 := -1
for i := 0; i < len(dis); i++ {
dis[i] = oil[i] - dis[i]
if dis[i] >= 0 {
init0 = i
}
}
return init0
}
func enlargeArea(dis []int, init0 int) []bool {
res := make([]bool, len(dis))
start := init0
end := nextIndex(init0, len(dis))
need := 0
rest := 0
for {
// 当前来到的start已经在连通区域中,可以确定后续的开始点一定无法转完一圈
if start != init0 && start == lastIndex(end, len(dis)) {
break
}
// 当前来到的start不在连通区域中,就扩充连通区域
// start(5) -> 联通区的头部(7) -> 2
// start(-2) -> 联通区的头部(7) -> 9
if dis[start] < need { // 当前start无法接到连通区的头部
need -= dis[start]
} else { // 当前start可以接到连通区的头部,开始扩充连通区域的尾巴
// start(7) -> 联通区的头部(5)
rest += dis[start] - need
need = 0
for rest >= 0 && end != start {
rest += dis[end]
end = nextIndex(end, len(dis))
}
// 如果连通区域已经覆盖整个环,当前的start是良好出发点,进入2阶段
if rest >= 0 {
res[start] = true
connectGood(dis, lastIndex(start, len(dis)), init0, res)
break
}
}
start = lastIndex(start, len(dis))
if start == init0 {
break
}
} //for (start != init0);
return res
}
// 已知start的next方向上有一个良好出发点
// start如果可以达到这个良好出发点,那么从start出发一定可以转一圈
func connectGood(dis []int, start int, init0 int, res []bool) {
need := 0
for start != init0 {
if dis[start] < need {
need -= dis[start]
} else {
res[start] = true
need = 0
}
start = lastIndex(start, len(dis))
}
}
func lastIndex(index int, size int) int {
return twoSelectOne(index == 0, (size - 1), index-1)
}
func nextIndex(index int, size int) int {
return twoSelectOne(index == size-1, 0, index+1)
}
执行结果如下:
拓展题:
前后端数据如何统一验证呢?比如前端输入要求6-16个字符,后端也做6-16个字符的验证。前后端分开验证,前后端都得开发,工作量增加。如何做到前端做验证,后端验证直接使用前端的规则?这样后端就不用开发了,工作量就减少了。
用js写验证api,前后端调用。
js、joi、request-validate、jsonschma、swagger。