两个栈实现队列
class MyQueue {
public:
stack<int> In;
stack<int> Out;
MyQueue() {
}
void push(int x) {
In.push(x);
}
int pop() {
if(Out.empty())
{
while(!In.empty())
{
Out.push(In.top());
In.pop();
}
}
int res = Out.top();
Out.pop();
return res;
}
int peek() {
int res = this->pop();
Out.push(res);
return res;
}
bool empty() {
return In.empty() && Out.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
两个队列实现栈
class MyStack {
public:
queue<int> Q1;
queue<int> Q2;
MyStack() {
}
void push(int x) {
Q1.push(x);
}
int pop() {
int size = Q1.size();
size--;
while(size--)
{
Q2.push(Q1.front());
Q1.pop();
}
int res = Q1.front();
Q1.pop();
Q1=Q2;
while(!Q2.empty())
{
Q2.pop();
}
return res;
}
int top() {
return Q1.back();
}
bool empty() {
return Q1.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
一个队列模拟栈
class MyStack {
public:
queue<int> q1;
MyStack() {
}
void push(int x) {
q1.push(x);
}
int pop() {
int size = q1.size();
size--;
while(size--)
{
q1.push(q1.front());
q1.pop();
}
int res = q1.front();
q1.pop();
return res;
}
int top() {
return q1.back();
}
bool empty() {
return q1.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
买卖股票的最佳时机II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
贪心
class Solution {
public:
int maxProfit(vector<int>& prices) {
int res = 0;
for(int i = 0;i < prices.size() - 1;++i)
{
res += max(prices[i+1] - prices[i],0);
}
return res;
}
};
最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例: 输入: [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
1.暴力:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int count = 0;
int res = INT_MIN;
for(int i = 0;i < nums.size();++i)
{
count = 0;
for(int j = i;j < nums.size();++j)
{
count += nums[j];
res = count > res? count : res;
}
}
return res;
}
};
2.贪心
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int Max = 0;
int res = nums[0];
for(auto &i:nums)
{
Max = max(Max + i,i);
res = max(res,Max);
}
return res;
}
};
摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1:
输入: [1,7,4,9,2,5]
输出: 6
解释: 整个序列均为摆动序列。
示例 2:
输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。
示例 3:
输入: [1,2,3,4,5,6,7,8,9]
输出: 2
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int cur = 0;//当前差值
int pre = 0;//前一个差值
int res = 1;//峰值个数
if(nums.size() == 1) return nums.size();
for(int i = 0; i < nums.size() - 1; ++i)
{
cur = nums[i+1] - nums[i];
if((cur > 0 && pre <= 0) || (cur < 0 && pre >= 0))
{
res++;
pre = cur;
}
}
return res;
}
};
分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1 解释:你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。所以你应该输出1。
示例 2:
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。你拥有的饼干数量和尺寸都足以让所有孩子满足。所以你应该输出2.
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int size = s.size() - 1;//饼干的下标
int res = 0;
for(int i = g.size() - 1; i >= 0;i--)
{
if(size >= 0 && s[size] >= g[i])
{
size--;
res++;
}
}
return res;
}
};
跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover = 0;
if(nums.size() == 1) return true;
for(int i = 0; i <= cover;++i)
{
cover = max(i + nums[i],cover);
if(cover >= nums.size() - 1) return true;
}
return false;
}
};
跳跃游戏II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 1) return 0;
int Curcover = 0;
int Nextcover = 0;
int res = 0;
for(int i = 0;i < nums.size();++i)
{
Nextcover = max(i + nums[i],Nextcover);
if(i == Curcover)
{
if(Curcover != nums.size() - 1)//当前距离无法覆盖整个数组
{
res++;
Curcover = Nextcover;
if(Nextcover >= nums.size() - 1) break;
}
else break;
}
}
return res;
}
};
K次取反后最大化的数组和
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。
示例 1:
输入:A = [4,2,3], K = 1
输出:5
解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。
示例 2:
输入:A = [3,-1,0,2], K = 3
输出:6
解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。
示例 3:
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。
思路:
第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
第二步:从前向后遍历,遇到负数将其变为正数,同时K–
第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
第四步:求和
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
int res = 0;
sort(nums.begin(),nums.end(),[](int a,int b){return abs(a) > abs(b);});
for(int i = 0;i < nums.size();++i)
{
if(nums[i] < 0 && k > 0)
{
nums[i] *= -1;
k--;
}
}
if(k % 2 == 1) nums[nums.size() -1] *= -1;
for(auto i:nums)
{
res += i;
}
return res;
}
};
加油站问题(贪心)
原题链接
1.暴力:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost)
{
for (int i = 0; i < cost.size(); ++i)
{
int res = gas[i] - cost[i];
int index = (i + 1) % cost.size();
while (res > 0 && index != i)
{
res += gas[index] - cost[index];
index = (index + 1) % cost.size();
}
if (res >= 0 && index == i) return i;
}
return -1;
}
};
2.贪心(全局贪心):
思路:
情况一:如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能这个负数填平,能把这个负数填平的节点就是出发节点。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int res = 0;
int MIN = INT_MAX;
for(int i = 0;i < gas.size();++i)
{
int abc = gas[i] - cost[i];
res += abc;
if(res < MIN) MIN = res;
}
if(res < 0) return -1;
if(MIN >= 0) return 0;
for(int i = gas.size() - 1;i >= 0;i--)
{
int abc = gas[i] - cost[i];
MIN += abc;
if(MIN >= 0) return i;
}
return -1;
}
};
贪心(局部最优)
思路:
首先看每一次加油与使用油的差值总和是否大于0,如果大于0,则一定存在从某一点出发使得可以走完全程,否则就不存在;
统计当前的差值总和,如果小于0,则起点一定在当前位置的下一个位置。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int cur = 0;
int total = 0;
int start = 0;
for(int i = 0;i < gas.size();++i)
{
cur += gas[i] - cost[i];
total += gas[i] - cost[i];
if(cur < 0)
{
start = i + 1;
cur = 0;
}
}
if(total >= 0) return start;
return -1;
}
};
分发糖果
原题链接
思路:
两次贪心
第一次:左<右 从左到右遍历,则局部贪心为arr[i] = arr[i-1] + 1;
第二次:左>右 从右向左遍历,因为如果从前向后遍历,根据 ratings[i + 1] 来确定 ratings[i] 对应的糖果,那么每次都不能利用上前一次的比较结果,此时arr[i]有两个选择 (1)上次循环右大于左所得的糖果,(2)arr[i+1] +1 的糖果,取最大 则可以推出局部最优为 arr[i] = max(arr[i],arr[i + 1] + 1);
class Solution {
public:
int candy(vector<int>& ratings) {
vector<int> arr(ratings.size(),1);
for(int i = 1;i < ratings.size();++i)//左 < 右
{
if(ratings[i] > ratings[i-1]) arr[i] = arr[i - 1] + 1;
}
for(int i = ratings.size() - 2;i >= 0;i--)
{
if(ratings[i] > ratings[i+1])
arr[i] = max(arr[i],arr[i + 1] + 1);
}
int res = 0;
for(int i:arr)
{
res += i;
}
return res;
}
};
柠檬水找零
原题链接
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int Sum_five = 0,Sum_ten = 0,Sum_twanty = 0;
for(auto i:bills)
{
if(i == 5) Sum_five++;
if(i == 10)
{
if(Sum_five == 0) return false;
Sum_five--;
Sum_ten++;
}
if(i == 20)
{
if(Sum_ten > 0 && Sum_five > 0)
{
Sum_ten--;
Sum_five--;
}
else if(Sum_five >= 3)
{
Sum_five -= 3;
}
else return false;
}
}
return true;
}
};
根据身高重建队列
原题链接
思路:
如果按照k来从小到大排序,排完之后,会发现k的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。
那么按照身高h来排序呢,身高一定是从大到小排(身高相同的话则k小的站前面),让高个子在前面。
此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高!
按照身高排序之后,优先按身高高的people的k来插入,后序插入节点也不会影响前面已经插入的节点,最终按照k的规则完成了队列。
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(),people.end(),[](vector<int> a,vector<int> b){if(a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];});
vector<vector<int>> que;
for(int i = 0;i < people.size();++i)
{
int pos = people[i][1];
que.insert(que.begin() + pos,people[i]);
}
return que;
}
};
用最少数量的箭引爆气球
原题链接
思路:数组排序,从前向后遍历气球数组,靠左尽可能让气球重复
如果气球重叠了,重叠区间一定需要一个弓箭。
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
if (points.size() == 0) return 0;
sort(points.begin(), points.end(), [](vector<int>& a, vector<int>& b) {return a[0] < b[0]; });
int res = 1;
for (int i = 1; i < points.size(); ++i)
{
if (points[i][0] > points[i - 1][1])
res++;
else points[i][1] = min(points[i - 1][1], points[i][1]);
}
return res;
}
};