LeetCode刷题笔记

面试经典150题

1. 数组/字符串

1.1 合并两个有序数组

题目

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1nums2 中的元素数目。
请你 合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n
示例 1:
 输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
 输出:[1,2,2,3,5,6]
 解释:需要合并 [1,2,3] 和 [2,5,6] 。
 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
 输入:nums1 = [1], m = 1, nums2 = [], n = 0
 输出:[1]
 解释:需要合并 [1] 和 [] 。
 合并结果是 [1] 。
示例 3:
 输入:nums1 = [0], m = 0, nums2 = [1], n = 1
 输出:[1]
 解释:需要合并的数组是 [] 和 [1] 。
 合并结果是 [1] 。
 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

题解
方法1

nums2赋值到nums1的后半部分,再调用Arrays的sort方法。

public static int[] merge_1(int[] nums1, int m, int[] nums2, int n) {
    for (int i = 0; i < n; i++) {
        nums1[m + i] = nums2[i];
    }
    Arrays.sort(nums1);
    return nums1;
}
方法2

借鉴归并排序的思想。从末尾开始,从大到小排序。

public static int[] merge_2(int[] nums1, int m, int[] nums2, int n) {
    int i = m - 1, j = n - 1, k = m + n - 1;

    while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]) {
            nums1[k--] = nums1[i--];
        } else {
            nums1[k--] = nums2[j--];
        }
    }
    return nums1;
}

1.2移除元素

题目

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
 输入:nums = [3,2,2,3], val = 3
 输出:2, nums = [2,2]
 解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
 输入:nums = [0,1,2,2,3,0,4,2], val = 2
 输出:5, nums = [0,1,3,0,4]
 解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

题解

遍历int数组,将不等于val的值加入到新的list中。循环赋值,返回结果。

public static ResultType removeElement(int[] nums, int val) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int num : nums) {
        if (num != val) {
            list.add(num);
        }
    }
    for (int i = 0; i < list.size(); i++) {
        nums[i] = list.get(i);
    }

    return new ResultType(list.size(), list);
}

1.3 删除有序数组中的重复项

题目

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k
示例 1:
 输入:nums = [1,1,2]
 输出:2, nums = [1,2,_]
 解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
 输入:nums = [0,0,1,1,1,2,2,3,3,4]
 输出:5, nums = [0,1,2,3,4]
 解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

题解

利用快慢指针(双指针)。慢指针用来锁定满足条件的元素。快指针检索相邻的元素是否相等,如果不相等,则将快指针指向的元素赋值给慢指针指向的元素。

public static ResultType removeDuplicates(int[] nums) {
    // Double pointer
    int slow=1;
    for (int fast = 1; fast < nums.length; fast++) {
        if(nums[fast]!=nums[fast-1])
            nums[slow++]=nums[fast];
    }
    return new ResultType(slow, nums);
}

1.4 删除有序数组中的重复项 II ***

题目

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
 输入:nums = [1,1,1,2,2,3]
 输出:5, nums = [1,1,2,2,3]
 解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3。 不需要考虑数组中超出新长度后面的元素。
示例 2:
 输入:nums = [0,0,1,1,1,1,2,3,3]
 输出:7, nums = [0,0,1,1,2,3,3]
 解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3。不需要考虑数组中超出新长度后面的元素。

题解
方法1

经典双指针。

public static ResultType removeDuplicates_1(int[] nums) {
    int n = nums.length;
    if (n <= 2) {
        return new ResultType(-1, nums);
    }
    int slow = 2, fast = 2;
    while (fast < n) {
        if (nums[slow - 2] != nums[fast]) {
            nums[slow] = nums[fast];
            ++slow;
        }
        ++fast;
    }
    return new ResultType(slow, nums);
}
方法2

使用计数法。

public static ResultType removeDuplicates_2(int[] nums) {
    // skip 1st and 2nd element
    int count = 2;
    for(int i = 2 ; i < nums.length ; i++) {
        if(nums[i] != nums[count-2]) {
            nums[count++] = nums[i];
        }
    }
    return new ResultType(count, nums);
}

1.5 多数元素

题目

给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
 输入:nums = [3,2,3]
 输出:3
示例 2:
 输入:nums = [2,2,1,1,1,2,2]
 输出:2

题解
方法1

摩尔投票

public static int majorityElement(int[] nums) {
            //诸王争霸赛开始【规则是目前投票数为0的话换候选人,自己人给自己人投票,敌方减票】
            //摩尔投票法为啥成立?因为这里的众数是指大于总数数目的二分之一,举两个个极端例子
            //121311【肯定有相邻的,其他的】或者111123【全部联合起来,敌方都抵消不了】
            int num = nums[0];//我先来做霸王
            int cnt = 1;//目前帮派就我一个人,遍历下去看看还有没有自己人为自己撑腰打气,首先遇到对手就被搞下去了
            for(int i = 1; i < nums.length; ++i){
                if(nums[i] == num){
                    cnt++;//帮派的人来撑腰了,票数++
                }
                else{
                    cnt--;//敌方来骚扰我当霸王,票数--
                    if(cnt == 0){//没了,目前帮派人不够地方多,话语权没有
                        num = nums[i];//更换霸王
                        cnt = 1;//新的霸王重新计数
                    }
                }
            }
            //选出来笑到最后的霸王
            return num;
        }
方法2

排序。如果将数组 nums 中的所有元素按照单调递增或单调递减的顺序排序,那么下标为\(\frac{n}{2}\)的元素一定是众数

public static int majorityElement_2(int[] nums) {
    Arrays.sort(nums);
    return nums[nums.length / 2];
}

1.6 旋转数组

题目

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
 输入: nums = [1,2,3,4,5,6,7], k = 3
 输出: [5,6,7,1,2,3,4]
 解释:
 向右轮转 1 步: [7,1,2,3,4,5,6]
 向右轮转 2 步: [6,7,1,2,3,4,5]
 向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
 输入:nums = [-1,-100,3,99], k = 2
 输出:[3,99,-1,-100]
 解释:
 向右轮转 1 步: [99,-1,-100,3]
 向右轮转 2 步: [3,99,-1,-100]

题解
方法1

把数组复制成两倍长度,右移k位就是从第n-k位往后的n个数字(n为数组长度)。

public static int[] rotate_1(int[] nums, int k) {
    int n = nums.length;
    k %= n;
    // initialize 2*n size int array
    int[] arr = new int[n << 1];
    System.arraycopy(nums, 0, arr, 0, n);
    System.arraycopy(nums, 0, arr, n, n);
    System.arraycopy(arr, n - k, nums, 0, n);

    return nums;
}
方法2

使用额外的数组来将每个元素放至正确的位置。用 n 表示数组的长度,我们遍历原数组,将原数组下标为 i 的元素放至新数组下标为 (i+k) mod n 的位置,最后将新数组拷贝至原数组即可。

public static int[] rotate_2(int[] nums, int k) {
    int n = nums.length;
    int[] newArr = new int[n];
    for (int i = 0; i < n; ++i) {
        newArr[(i + k) % n] = nums[i];
    }
    System.arraycopy(newArr, 0, nums, 0, n);

    return nums;
}

1.7 买卖股票的最佳时机

题目

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

 输入:[7,1,5,3,6,4]
 输出:5
 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:

 输入:prices = [7,6,4,3,1]
 输出:0
 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

题解
方法1

双指针。prices[slow]获取数组的最小值,这里的slow一旦找到最小值就固定了。然后向后遍历数组,取prices[fast]和prices[slow]最大值。

public static int maxProfit(int[] prices) {
    int max=0,slow=0,fast=1;
    if (prices.length <=1){ return max;}
    while (fast<prices.length){
        if (prices[slow]>=prices[fast]){
            slow = fast;
        }else {
            max = Math.max(max,prices[fast]-prices[slow]);
        }
        fast++;
    }

    return max;
}
方法2

简单直观。

public static int maxProfit_2(int[] prices) {
    int minprice = Integer.MAX_VALUE;
    int maxprofit = 0;
    for (int i = 0; i < prices.length; i++) {
        if (prices[i] < minprice) {
            minprice = prices[i];
        } else if (prices[i] - minprice > maxprofit) {
            maxprofit = prices[i] - minprice;
        }
    }
    return maxprofit;
}

1.8 买卖股票的最佳时机 II

题目

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候最多只能持有 一股 股票。你也可以先购买,然后在同一天出售。
返回你能获得的最大利润。
示例 1:
 输入:prices = [7,1,5,3,6,4]
 输出:7
 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。总利润为 4 + 3 = 7。
示例 2:
 输入:prices = [1,2,3,4,5]
 输出:4
 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。总利润为 4。
示例 3:
 输入:prices = [7,6,4,3,1]
 输出:0
 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0。

题解
方法1
public static int maxProfit_1(int[] prices) {
    int result = 0;
    for (int i = 1; i < prices.length; i++) {
        result += Math.max(prices[i] - prices[i - 1], 0);
    }
    return result;
}
方法2
public static int maxProfit_2(int[] prices) {
    int revenue = 0;
    for (int i = 1; i < prices.length; i++) {
        if(prices[i]>prices[i-1])
            revenue += prices[i]-prices[i-1];

    }
    return revenue;
}

1.9 跳跃游戏

题目

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false
示例 1:
 输入:nums = [2,3,1,1,4]
 输出:true
 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
 输入:nums = [3,2,1,0,4]
 输出:false
 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

题解

贪心算法。来自官方题解:
设想一下,对于数组中的任意一个位置 \(y\),我们如何判断它是否可以到达?根据题目的描述,只要存在一个位置 \(x\),它本身可以到达,并且它跳跃的最大长度为 \(x+nums[x]\),这个值大于等于 \(y\),即 \(x+nums[x]≥y\),那么位置 \(y\) 也可以到达。

换句话说,对于每一个可以到达的位置 \(x\),它使得 \(x+1\),\(x+2\),⋯ ,\(x+nums[x]\)这些连续的位置都可以到达。

这样以来,我们依次遍历数组中的每一个位置,并实时维护最远可以到达的位置。对于当前遍历到的位置 \(x\),如果它在 最远可以到达的位置的范围内,那么我们就可以从起点通过若干次跳跃到达该位置,因此我们可以用 \(x+nums[x]\) 更新 最远可以到达的位置。

在遍历的过程中,如果 最远可以到达的位置 大于等于数组中的最后一个位置,那就说明最后一个位置可达,我们就可以直接返回 True 作为答案。反之,如果在遍历结束后,最后一个位置仍然不可达,我们就返回 False 作为答案。

public static boolean canJump(int[] nums) {
    int n = nums.length;
    int rightmost = 0;
    for (int i = 0; i < n; ++i) {
        if (i <= rightmost) {
            rightmost = Math.max(rightmost, i + nums[i]);
            if (rightmost >= n - 1) {
                return true;
            }
        }
    }
    return false;
}

1.10 跳跃游戏 II

题目

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

示例 1:
 输入: nums = [2,3,1,1,4]
 输出: 2
 解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
 输入: nums = [2,3,0,1,4]
 输出: 2

题解
方法1

官方题解:
我们的目标是到达数组的最后一个位置,因此我们可以考虑最后一步跳跃前所在的位置,该位置通过跳跃能够到达最后一个位置。
如果有多个位置通过跳跃都能够到达最后一个位置,那么我们应该如何进行选择呢?直观上来看,我们可以「贪心」地选择距离最后一个位置最远的那个位置,也就是对应下标最小的那个位置。因此,我们可以从左到右遍历数组,选择第一个满足要求的位置。
找到最后一步跳跃前所在的位置之后,我们继续贪心地寻找倒数第二步跳跃前所在的位置,以此类推,直到找到数组的开始位置。

public static int jump(int[] nums) {
    int position = nums.length - 1;
    int steps = 0;
    while (position > 0) {
        for (int i = 0; i < position; i++) {
            if (i + nums[i] >= position) {
                position = i;
                steps++;
                break;
            }
        }
    }
    return steps;
}

方法2

如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。
例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。
从下标 1 出发,最远可到达下标 4。下标 1 可到达的位置中,下标 4 的值是 4 ,从下标 4 出发可以达到更远的位置,因此第二步到达下标 4。

在具体的实现中,我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。
在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。

public static int jump_2(int[] nums) {
    int length = nums.length;
    int end = 0;
    int maxPosition = 0;
    int steps = 0;
    for (int i = 0; i < length - 1; i++) {
        maxPosition = Math.max(maxPosition, i + nums[i]);
        if (i == end) {
            end = maxPosition;
            steps++;
        }
    }
    return steps;
}

1.11 H指数

题目

给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。
根据维基百科上 h 指数的定义:h 代表“高引用次数” ,一名科研人员的 h 指数 是指他(她)至少发表了 h 篇论文,并且 至少 有 h 篇论文被引用次数大于等于 h 。如果 h 有多种可能的值,h 指数 是其中最大的那个。
示例 1:
 输入:citations = [3,0,6,1,5]
 输出:3
 解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。
由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。
示例 2:
 输入:citations = [1,3,1]
 输出:1

题解

首先我们可以将初始的 \(H\) 指数 \(h\) 设为 0,然后将引用次数排序,并且对排序后的数组从大到小遍历。
根据 \(H\) 指数的定义,如果当前 \(H\) 指数为 \(h\) 并且在遍历过程中找到当前值 \(citations[i]>h\),则说明我们找到了一篇被引用了至少 \(h+1\) 次的论文,所以将现有的 \(h\) 值加 1。继续遍历直到 \(h\) 无法继续增大。最后返回 \(h\) 作为最终答案。

public static int hIndex(int[] citations) {
    Arrays.sort(citations);
    int h = 0, i = citations.length - 1;
    while (i >= 0 && citations[i] > h) {
        h++;
        i--;
    }
    return h;
}  

1.12 O(1) 时间插入、删除和获取随机元素 ***

题目

实现RandomizedSet 类:
RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)

题解
static class RandomizedSet {
    List<Integer> nums;
    Map<Integer, Integer> indices;
    Random random;

    public RandomizedSet() {
        nums = new ArrayList<Integer>();
        indices = new HashMap<Integer, Integer>();
        random = new Random();
    }

    public boolean insert(int val) {
        if (indices.containsKey(val)) {
            return false;
        }
        int index = nums.size();
        nums.add(val);
        indices.put(val, index);
        return true;
    }

    public boolean remove(int val) {
        if (!indices.containsKey(val)) {
            return false;
        }
        int index = indices.get(val);
        int last = nums.get(nums.size() - 1);
        nums.set(index, last);
        indices.put(last, index);
        nums.remove(nums.size() - 1);
        indices.remove(val);
        return true;
    }

    public int getRandom() {
        int randomIndex = random.nextInt(nums.size());
        return nums.get(randomIndex);
    }
}

1.13 除自身以外数组的乘积

题目

给你一个整数数组nums,返回数组answer,其中answer[i]等于nums中除nums[i]之外其余各元素的乘积。
请不要使用除法,且在O(n)时间复杂度内完成此题。
示例 1:
 输入: nums = [1,2,3,4]
 输出: [24,12,8,6]
 示例 2:

 输入: nums = [-1,1,0,-3,3]
 输出: [0,0,9,0,0]

题解

官方题解:
我们不必将所有数字的乘积除以给定索引处的数字得到相应的答案,而是利用索引左侧所有数字的乘积和右侧所有数字的乘积(即前缀与后缀)相乘得到答案。

对于给定索引i,我们将使用它左边所有数字的乘积乘以右边所有数字的乘积。下面让我们更加具体的描述这个算法。

public static int[] productExceptSelf(int[] nums) {
    int length = nums.length;

    // left array and right array
    int[] L = new int[length];
    int[] R = new int[length];

    int[] answer = new int[length];

    // L[i] is the product of all elements on the left of nums[i]
    // there is no element on the left of nums, so L[0] = 1
    L[0] = 1;
    for (int i = 1; i < length; i++) {
        L[i] = nums[i - 1] * L[i - 1];
    }

    // R[i] is the product of all elements on the right of nums[i]
    // there is no element on the right of nums, so R[length-1] = 1
    R[length - 1] = 1;
    for (int i = length - 2; i >= 0; i--) {
        R[i] = nums[i + 1] * R[i + 1];
    }

    // For index i, the product of all elements except nums[i] is the product of all elements on the left multiplied by the product of all elements on the right
    for (int i = 0; i < length; i++) {
        answer[i] = L[i] * R[i];
    }

    return answer;
}

1.14 加油站***

题目

在一条环路上有n个加油站,其中第i个加油站有汽油gas[i]升。
你有一辆油箱容量无限的的汽车,从第i个加油站开往第i+1个加油站需要消耗汽油cost[i]升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组gascost,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回-1。如果存在解,则保证它是唯一的。
示例 1:
 输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
 输出: 3
 解释:
 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
 因此,3 可为起始索引。
示例 2:
 输入: gas = [2,3,4], cost = [3,4,3]
 输出: -1
 解释:
 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
 我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
 开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
 开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
 你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
 因此,无论怎样,你都不可能绕环路行驶一周。

题解
public static int canCompleteCircuit(int[] gas, int[] cost) {
    int n = gas.length;
    int i = 0;
    while (i < n) {
        int sumOfGas = 0, sumOfCost = 0;
        int cnt = 0;
        while (cnt < n) {
            int j = (i + cnt) % n;
            sumOfGas += gas[j];
            sumOfCost += cost[j];
            if (sumOfCost > sumOfGas) {
                break;
            }
            cnt++;
        }
        if (cnt == n) {
            return i;
        } else {
            i = i + cnt + 1;
        }
    }
    return -1;
}
posted @ 2024-01-07 03:10  Rest探路者  阅读(6)  评论(0编辑  收藏  举报
levels of contents