目录
1.简单
2.中等
2.1买卖股票的最佳时机Ⅱ
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
典型的贪心算法,先分别求局部最优解,然后将局部最优解汇总。采用双指针法,min在前,max在后,当min找到当前局部的最小值(比如示例中的 1 ),max从min后再找局部最大值(示例中的5),p[max] - p[min]即为一个局部最优解(注意指针越界的情况)。
int maxProfit(vector<int>& prices) {
int min = 0, max = 1;
int profit = 0;
int len = prices.size();
while(min < len && max < len){
while(min < len - 1 && prices[min + 1] <= prices[min]) //寻找局部最小值
min++;
max = min + 1;
while(max < len - 1 && prices[max + 1] >= prices[max]) //寻找局部最大值
max++;
if(min < len && max < len){ //局部最优解
profit += prices[max] - prices[min];
min = max + 1;
max += 2;
}
}
return profit;
}
2.2跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
(面试的时候我可能真的做不出来=_=)
思路:尽可能到达最远位置(贪心)。
如果能到达某个位置,那一定能到达它前面的所有位置。
方法:初始化最远位置为0,然后遍历数组,如果当前位置能到达,并且当前位置+跳数>最远位置,就更新最远位置。最后比较最远位置和数组长度。
复杂度:时间复杂度O(n),空间复杂度O(1)。
作者:mo-lan-4
链接:https://leetcode-cn.com/problems/jump-game/solution/pythonji-bai-97kan-bu-dong-ni-chui-wo-by-mo-lan-4/
bool canJump(vector<int>& nums) {
int max_jump = 0;
for(int i = 0; i < nums.size(); i++){
if(max_jump >= i)
max_jump = max(max_jump, i + nums[i]);
}
return max_jump >= nums.size() - 1;
}
回溯算法虽然可以做,但会超时
bool traceback(vector<int>& nums, int begin){
if(begin == nums.size() - 1){
return true;
}
int furthest = nums[begin] + begin;
if(nums[begin] + begin > nums.size() - 1)
furthest = nums.size() - 1;
for(int i = begin + 1; i <= furthest; i++){
if(traceback(nums, i)){
return true;
}
}
return false;
}
bool canJump(vector<int>& nums){
return traceback(nums, 0);
}
2.3加油站
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/gas-station
暴力法:时间复杂度O(n),把每一个位置当为开始,看能不能走完这一圈;每次比较剩余汽油+加油>=路程才继续
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int stock = 0;
int flag = 0;
for (int i = 0; i < gas.size(); ++i) {
if(cost[i] > gas[i])
continue;
int j;
for(j = i; j < gas.size(); j++){
stock = stock + gas[j] - cost[j];
if(stock < 0){
stock = 0;
flag = 1;
break;
}
}
if(flag == 1){
flag = 0;
continue;
}
for(j = 0; j < i; j++){
stock = stock + gas[j] - cost[j];
if(stock < 0){
stock = 0;
break;
}
}
if(j == i)
return i;
}
return -1;
}
看了0ms的范例代码,确实佩服,只需要依次遍历,就能把开始位置找出来。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int currentTank = 0;
int totalTank = 0;
int start = 0;
for (int i = 0; i < gas.size(); i++) {
totalTank += gas[i] - cost[i]; //如果totalTank最终<0,那肯定是走不通
currentTank += gas[i] - cost[i];
if (currentTank < 0) { //从上一个start开始其实走不通,就把start变为i + 1尝试
start = i + 1;
currentTank = 0;
}
}
return totalTank < 0 ? -1 : start;
}
};
3.困难
3.1跳跃游戏Ⅱ
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:
假设你总是可以到达数组的最后一个位置。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-ii
之前的跳跃游戏I是用的回溯法,尽最大可能去尝试,不会超时,但不适用跳跃游戏II。这道题默认可以到达最终位置,所以采用贪心算法,查找每次能到达的最远位置作为下次搜索的end,再把这次的end赋值给下一次的begin,直到搜索到结尾。
int jump(vector<int>& nums){
int begin = 0, end = 1, count = 0;
while (end < nums.size()){
int temp = 0;
for (int i = begin; i < end; ++i) {
temp = max(temp, i + nums[i]);
}
begin = end;
end = temp + 1;
count++;
}
return count;
}