2021-09-04:加油站。在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。说明: 如果题目有解,该答案即为唯一答案。输入数组均为非空数组,且长度相同。输入数组中的元素均为非负数。力扣134。
福大大 答案2021-09-04:
纯能数组。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个字符的验证。前后端分开验证,前后端都得开发,工作量增加。如何做到前端做验证,后端验证直接使用前端的规则?这样后端就不用开发了,工作量就减少了。
福大大 答案2021-09-04:
用js写验证api,前后端调用。
js、joi、request-validate、jsonschma、swagger。