加油站的良好出发点问题

news/2024/5/20 9:06:21 标签: leetcode, 算法, 数据结构, 滑动窗口

加油站的良好出发点问题

作者:Grey

原文地址:

博客园:加油站的良好出发点问题

CSDN:加油站的良好出发点问题

题目描述

题目链接

思路

暴力解法 O(N^2)

我们可以通过生成辅助数组来验证良好出发点

int[]h

这个数组的长度和cost数组长度一致,且这个数组的每个元素的生成逻辑是:

h[i]=gas[i]-cost[i];

我们可以很容易得到一个结论:h(i) 往后累加,并回到i位置,不出现负数,就是良好出发点 ,这个i位置就是良好出发点。

以每个位置作为i位置,依次走这个逻辑,所以这个解法的复杂度是 O(N^2),代码如下:

    public int canCompleteCircuit(int[] gas, int[] cost) {
        int len = cost.length;
        int[] helper = new int[len];
        for (int i = 0; i < helper.length; i++) {
            helper[i] = gas[i] - cost[i];
        }
        int pre = 0;
        for (int i = 0; i < len; i++) {
            pre = helper[i];
            if (pre < 0) {
                continue;
            }
            for (int j = i + 1; j < len + i + 1; j++) {
                pre += helper[j < len ? j : (j - len)];
                if (pre < 0) {
                    break;
                }
            }
            if (pre >= 0) {
                return i;
            }
        }
        return -1;
    }

滑动窗口 时间复杂度 O(N) 空间复杂度 O(N)

首先,我们还是需要生成h[i]数组

h[i]=gas[i]-cost[i];

假设生成的h[i]数组如下:

[1,-1,0,3,-1]

我们生成其累加和数组preSum[i]

[1,0,0,3,2]

用这个累加和数组再和h[i]数组相加,得到一个两倍长度的数组

[1,0,0,3,2,3,2,2,5,4]

求针对这个数组,滑动窗口为n(n为原数组长度)的最小值,如果第i个窗口内的最小值减去窗口前一个位置的值小于0,则i号位置不是良好出发点

比如

L...L + n - 1是第x个窗口,最小值m

如果:

m - h[L-1] >= 0

则x是良好出发点

反之,则x不是良好出发点, 完整代码:

public static int canCompleteCircuit(int[] gas, int[] cost) {
        int len = gas.length;
        int doubleLen = len << 1;
        int[] h = new int[doubleLen];
        h[0] = gas[0] - cost[0];
        for (int i = 1; i < doubleLen; i++) {
            if (i < len) {
                h[i] = gas[i] - cost[i];
                h[i] += h[i - 1];
            }
            if (i >= len) {
                h[i] = h[len - 1] + h[i - len];
            }
        }
        LinkedList<Integer> qMin = new LinkedList<>();
        int r = 0;
        int index = 0;
        while (r < doubleLen) {
            while (!qMin.isEmpty() && h[qMin.peekLast()] >= h[r]) {
                qMin.pollLast();
            }
            qMin.addLast(r);
            if (qMin.peekFirst() == r - len) {
                qMin.pollFirst();
            }
            if (r >= len - 1) {
                if (r == len - 1) {
                    if (h[qMin.peekFirst()] >= 0) {
                        return index;
                    }
                } else {
                    if (h[qMin.peekFirst()] - h[r - len] >= 0) {
                        return index;
                    }
                }
                index++;
            }
            r++;
        }
        return -1;
    }

更进一步,如果每个位置都这样求,就可以得到每个位置是否是良好出发点,代码如下:

public static boolean[] canCompleteCircuitOfAllPositions(int[] gas, int[] cost) {
        int N = gas.length;
        int[] h = new int[N];
        for (int i = 0; i < N; i++) {
            h[i] = gas[i] - cost[i];
        }
        int R = N << 1;
        int[] doubleLenOfHelper = new int[R];
        doubleLenOfHelper[0] = h[0];
        for (int i = 1; i < N; i++) {
            doubleLenOfHelper[i] = h[i] + doubleLenOfHelper[i - 1];
        }
        for (int i = 0; i < N; i++) {
            doubleLenOfHelper[i + N] = h[i] + doubleLenOfHelper[i + N - 1];
        }
        LinkedList<Integer> q = new LinkedList<>();
        boolean[] res = new boolean[R - N + 1];
        int index = 0;
        for (int i = 0; i < R; i++) {
            while (!q.isEmpty() && doubleLenOfHelper[i] <= doubleLenOfHelper[q.peekLast()]) {
                q.pollLast();
            }
            q.addLast(i);
            if (q.peekFirst() == (i - N)) {
                q.pollFirst();
            }
            // 窗口已经形成了
            if (i >= N - 1) {
                if (i == N - 1) {
                    res[index++] = (doubleLenOfHelper[q.peekFirst()] >= 0);
                } else {
                    res[index++] = ((doubleLenOfHelper[q.peekFirst()] - doubleLenOfHelper[i - N]) >= 0);
                }
            }
        }
        // res[i]位置存着每个位置是否为良好出发点
        return res;
    }

更多

算法数据结构笔记

参考资料

算法数据结构体系学习班


http://www.niftyadmin.cn/n/1647777.html

相关文章

mysql 创建用户名及密码

CREATE USER 用户名 IDENTIFIED BY 密码; 如&#xff1a;CREATE USER kfcx IDENTIFIED BY kfcx123; 转载于:https://www.cnblogs.com/MUQINGFENG123/p/10861177.html

TypeError: ‘NoneType‘ object is not subscriptable

TypeError: ‘NoneType’ object is not subscriptable 错误的原因是&#xff1a;操作了None的变量&#xff0c;调用了None.方法&#xff0c;但空变量是没有方法的&#xff0c;所以报错&#xff1b;检查加个判断&#xff0c;当变量为空时&#xff0c;做另外的操作

不同种类软件的比较

作者&#xff1a;Grey 原文地址&#xff1a; 不同种类软件的比较 问题来源于《构建之法》第三版 P18页中的第4题 软件有很多种分类方法&#xff0c;下面是另一种&#xff1a; ShrinkWrap&#xff08;在包装盒子里面的软件&#xff09;、Web APP&#xff08;基于网页的软件&…

先验分布:(三)Dirichlet分布的应用——LDA模型

LDA(Latent Dirichlet Allocation)模型是Dirichlet分布的实际应用。 在自然语言处理中&#xff0c;LDA模型及其许多延伸主要用于文本聚类、分类、信息抽取和情感分析等。 例如&#xff0c;我们要对许多新闻按主题进行分类。目前用的比较多的方法是&#xff1a;假设每篇新闻都有…

设计并发算法的方法论

一. 什么是并发&#xff1f;和并行的区别&#xff1f; 单个处理器上采用单核处理多个任务即为并发&#xff0c;在这种情况下&#xff0c;操作系统的调度程序会频繁且迅速地从一个任务切换到另一个任务&#xff0c;因此看起来所有任务是同时进行的&#xff1b; 而并行是在不同的…

二叉树的先,中,后序遍历(递归,非递归)

二叉树的先&#xff0c;中&#xff0c;后序遍历(递归&#xff0c;非递归) 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;二叉树的先&#xff0c;中&#xff0c;后序遍历(递归&#xff0c;非递归) CSDN&#xff1a;二叉树的先&#xff0c;中&#xff0c;后…

NOIP复赛文件路径怎么写

以2018年NOIP普及组复赛为例&#xff0c;四道题对应着四个文件夹&#xff1a; 随便选一道题&#xff0c;比如第一道题&#xff0c;进入title目录&#xff0c;可以看到title1.in, title1.ans, title2.in, titles.ans。这四个文件放的是测试数据。title1.in放的是第一组输入数据&…

Git 推送到多个远程仓库

Git 推送到多个远程仓库 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;Git 推送到多个远程仓库 CSDN&#xff1a;Git 推送到多个远程仓库 针对新建项目 在GitCode和GitHub上分别新建两个不包括任何文件的空仓库 https://github.com/GreyZeng/algorithm.…