1 (dump game)给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
(从前往后跳) O(n)
每遍历到一个数,就计算一下从这个数开始跳所能到达的最远距离
如果遍历到i已经超过了前面计算出的最远距离,则无法到达
如果计算出的最远距离超过数组的最后一个元素位置,则可以到达
class Solution {
public:
bool canJump(vector<int>& nums) {
for(int i=0,maxdump=0;i<nums.size();i++){
if(i>maxdump)return false;
if(maxdump>=nums.size()-1)return true;
maxdump=max(maxdump,i+nums[i]);
}
return true;
}
};
2.(dump 2) 给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
(动态规划,贪心优化) O(n)
首先定义两个指针 last 和 i,数组 f[i] 表示到达 i 所需要的最少步数。
定义 last 为第一次到达 i 时上一步的位置,last 从 0 开始。
根据贪心得知,令 f[i] = f[last] + 1 后,f[i] 就会是最优值。
故可以根据 i 来让 last 向后移动,找到最早的可以一步到达 i 的位置,然后根据 f[last] 更新 f[i]。
class Solution {
public:
int jump(vector<int>& nums) {
int n=nums.size();
int f[n];
memset(f,0,sizeof f);
f[0]=0;
int last=0;
for(int i=1;i<n;i++){
while(i>nums[last]+last)
last++;
f[i]=f[last]+1;
}
return f[n-1];
}
};
3(Lemonade Change) 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。
顾客排队购买你的产品,(按账 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
如果你能给每位顾客正确找零,返回 true ,否则返回 false。
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five=0,ten=0;
for(int bill:bills){
if(bill==5){
five++;
}
if(bill==10){
if(five>=1){
ten++;
five--;
}
else
return false;
}
if(bill==20){
if(ten>=1&&five>=1){
ten--;
five--;
}
else if(five>=3){
five-=3;
}
else
return false;
}
}
return true;
}
};
4 (Is Subsequence) 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
(双指针+贪心) O(n+m)
题解1:双指针做法。使用i索引当前匹配到s[i],j索引当前匹配到t[j]。时间复杂度O(n+m)
class Solution {
public:
bool isSubsequence(string s, string t) {
int n=s.size(),m=t.size();
int i=0;
for(int j=0;i<n&&j<m;i++,j++){
while(j<m&&t[j]!=s[i])
{
j++;
}
if(j==m)return false;
}
return i==n;;
}
};
5 (Assign Cookies)假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int res=0;
for(int i=0,j=0;i<g.size()&&j<s.size();){
if(g[i]<=s[j]){
res++;
i++;
j++;
}
else{
j++;
}
}
return res;
}
};
6(Wiggle Subsequence)如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
(贪心) O(n)
- 首先对nums进行相邻去重,即用stl中的unique函数去除容器中相邻的重复元素(注意它是把重复元素放在容器末尾,返回值为去重之后的尾地址),再用erase函数进行删除
- 用一个for loop找极值点:如图,在一段连续上升(或下降)的线段中,只保留端点,可用反证法证明端点是最佳选择(如果选其他的,可能会使序列变短)。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int res=2;
nums.erase(unique(nums.begin(),nums.end()),nums.end());
if(nums.size()<=2)return nums.size();
for(int i=1;i+1<nums.size();i++){
if(nums[i]>nums[i-1]&&nums[i]>nums[i+1])
res++;
else if(nums[i]<nums[i-1]&&nums[i]<nums[i+1])
res++;
}
return res;
}
};
7(Queue Reconstruction by Height)假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。
编写一个算法来重建这个队列。总人数少于1100人,假设输入合法。
(贪心) O(n2)
思路:身高高的人只会看到比他高的人,所以当身高高的人固定好了位置,前面插入多少个矮的人都不会破坏高的人的条件限制。所以应该先决定高的人的位置,再决定矮的人的位置;高的人限制条件少,矮的人限制条件多。
- 先按身高从大到小排序,身高一样则按照k排序:身高大或k小意味着限制条件少,应该被优先考虑。
- 依次插入元素:由上一点,先进入res的元素不会被后进入的元素影响,因此每一次插入只需要考虑自己不需要考虑别人。当遍历到元素[a,b]的时候,比它大的元素已经进组,比它小的元素还没进组,那么它应该插到res的第b位,从而实现0到b-1的数字都比它大。
举例,输入是[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
排序后是[[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]
插入[7,0], res=[[7,0]]
插入[7,1], res=[[7,0],[7,1]]
插入[6,1], res=[[7,0],[6,1],[7,1]]
插入[5,0], res=[[5,0],[7,0],[6,1],[7,1]]
插入[5,2], res=[[5,0],[7,0],[5,2],[6,1],[7,1]]
插入[4,4], res=[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
bool cmp(const vector<int>&a,const vector<int>&b)
{
return a[0] > b[0] || (a[0] == b[0] && a[1] < b[1]);
}//注意写在class外
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
int n=people.size();
sort(people.begin(),people.end(),cmp);
vector<vector<int>> res;
for(auto p:people)
{
res.insert(res.begin()+p[1],p);
}
return res;
}
};
8(Minimum Number of Arrows to Burst Balloons) 在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在 104104 个气球。
一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 x_start,x_end, 且满足 x_start <= x <= x_end,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
(排序贪心) O(nlogn)
此题可以考虑将区间求交集,最后必定是一些不重叠的独立的区间,独立的区间个数就是答案数。具体做法如下:
- 首先将区间按照左端点从小到大排序,左端点相同的按照右端点从小到大排序。
- 设立两个值 start 和 end 代表当前飞镖可以放置的范围。
- 每当遇到一个新区间,若 end 小于新区间的起点,则需要一个新飞镖。否则原飞镖的区间 start 和新区间的起点取最大值,end和新区间的终点取最小值,即求交集。
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
int n=points.size();
if(n==0) return 0;
sort(points.begin(),points.end());
int res=1,start=points[0][0],end=points[0][1];
for(int i=1;i<n;i++){
if(points[i][0]>end){
res++;
start=points[i][0];
end=points[i][1];
}
else {
start=max(start,points[i][0]);
end=min(end,points[i][1]);
}
}
return res;
}
};
9(Remove K Digits ) 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
(单调栈,贪心)
如果我们当前遇到的数字比上一个数字要小的话,肯定是删除上一个数字比较划算。我们最多能删除k个字符。所以我们使用一个单调栈来存储每一个字符,如果当前读进来的数字比前一个数字小,我们就将栈顶元素出栈,直至出栈了k个字符或者栈顶元素已经比当前元素还小。这样在我们删除k个元素后,栈中元素就是剩下的数字啦。这时候我们需要考虑的就是删除前导0和空栈的情况啦。字符串有push和pop操作,所以我们可以直接用字符串来模拟栈,效果是一样的。
class Solution {
public:
string removeKdigits(string num, int k) {
stack<int> stk;
int n=num.size();
for(int i=0;i<n;i++){
while(stk.size()&&stk.top()>num[i]&&k){
stk.pop();
k--;
}
stk.push(num[i]);
}
while(k--)
stk.pop();
string res="";
while(stk.size()){
res+=stk.top();
stk.pop();
}
reverse(res.begin(),res.end());
int i=0;
while(i<res.size()&&res[i]=='0')
i++;
res=res.substr(i);
return res==""?"0":res;//可能全零那么字符串变为空,但要输出“0”
}
};