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)
}

执行结果如下:
在这里插入图片描述


左神java代码

拓展题:
前后端数据如何统一验证呢?比如前端输入要求6-16个字符,后端也做6-16个字符的验证。前后端分开验证,前后端都得开发,工作量增加。如何做到前端做验证,后端验证直接使用前端的规则?这样后端就不用开发了,工作量就减少了。

福大大 答案2021-09-04:

用js写验证api,前后端调用。
js、joi、request-validate、jsonschma、swagger。