一个月没更新了。。
最近近况:
h公司试用期的第二个月。压力挺重,每天基本都是各种加班,9点或者十点半的班车,甚至错过班车,从东莞回到宿舍都是10点以后的事。。实在没这个精力每天晚上都跑来这更新一下(吐槽一下公司内网保密不能更博,要不然平时上班时间闲暇的时候还可以更一下)
打卡还是有打的,一次性把这个月的所有题目都发出来就算了。。图也懒得贴了,主要写解题思路以及贴代码
2020.5.1 合并两个有序链表(简单)
思路:略
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode head(0);
ListNode* cur=&head;
while(l1!=NULL && l2!=NULL){
if(l1->val>l2->val)
{
cur->next=l2;
l2=l2->next;
cur=cur->next;
}else{
cur->next=l1;
l1=l1->next;
cur=cur->next;
}
}
if(l1!=NULL)
cur->next=l1;
if(l2!=NULL)
cur->next=l2;
return head.next;
}
};
2020.5.2 无重复字符的最长子串(中等)
思路:双指针 + set,如果set中已存在,则更新当前长度,同时移除重复字符自身以及之前的其他字符,统计新的子串长度(突然发现这道题在之前面试考过,当时是手写代码,然后面试官还给我说错题意了,按他的意思给了有问题的答案,在面试快结束之后才意识到不对,重新更正了一遍,并且指出错误,有点怀疑是故意给我设的坑)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
set<char> chars;
int L=0,R=0,maxLength=0;
while(R>=L && R<s.size()){
if(chars.find(s[R])==chars.end()){
chars.insert(s[R]);
maxLength=max(maxLength,R-L+1);
R++;
}else{
chars.erase(s[L]);
L++;
}
}
return maxLength;
}
};
2020.5.3 最大子序和(简单)
思路:从L到R的求和等价于0-R的求和 - 0-L的求和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int L=0,R=1,res=nums[0];
vector<int> sums(nums.size()+1,0);
for(int i=0;i<nums.size();i++){
sums[i+1]=sums[i]+nums[i];
}
while(L<=R && R<=nums.size()){
if(R>L && sums[R]-sums[L]>res)
res=sums[R]-sums[L];
cout<<L<<","<<R<<","<<sums[R] - sums[L]<<endl;
while(R<sums.size() && R>L && sums[R]-sums[L]<sums[R]-sums[L+1])
{
L++;
}
if((R+1<sums.size() && sums[R+1]-sums[L]>0)|| L>=R)
R++;
else
L++;
}
return res;
}
};
2020.5.4 跳跃游戏(困难)
思路:
当处在某个位置x时,这个位置的值是y则代表可以跳到x+1 - x+y这些范围之内,然后通过BFS
和是否访问的标志进行去重即可,当当前格子加上格子的值可以到达最后一个位置,则说明是最终解(到达终点是必然的,但是第一个到达终点的必然是步数最少的)
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size()<=1)
return 0;
queue<int> q,steps;
vector<int> visited(nums.size(),0);
q.push(0);
steps.push(0);
while(!q.empty()){
int pos=q.front();
int lastStep=steps.front();
q.pop();
steps.pop();
if(visited[pos])
continue;
visited[pos]=1;
if(pos+nums[pos]>=nums.size()-1)
return lastStep+1;
for(int i=nums[pos];i>=1;i--)
{
q.push(pos+i);
steps.push(lastStep+1);
}
}
return 0;
}
};
2020.5.5 验证二叉搜索树(中等)
思路:二叉搜索树:父节点比左节点值大,小于右节点值
同时,左节点和右节点也有另一个边界,如对于父节点的右节点的左子节点,值是在父节点和父节点的右节点之间
验证二叉搜索树的合法性:可以通过递归即可,每次为当前节点设定其范围值,以验证当前节点和当前节点的子节点
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root==NULL)
return 1;
return check(root->left,root->val,LONG_MIN)&&check(root->right,LONG_MAX,root->val);
}
bool check(TreeNode* cur,long max,long min){
if(cur==NULL)
return 1;
if(cur->val>min && cur->val<max){
return check(cur->left,cur->val,min) && check(cur->right,max,cur->val);
}else
return 0;
}
};
2020.5.6 最低票价(中等)
思路:动态规划,从价格低的票价开始,类似爬楼梯,每次只能爬固定的台阶
class Solution {
public:
int mincostTickets(vector<int>& days, vector<int>& costs) {
int costdays[3] = { 1, 7, 30 };
vector<int> dp(366, 0);
int day = 1;
for (int date : days){
while (day < date)
{
dp[day] = dp[day - 1];
day++;
}
int cost = INT_MAX;
for (int i = 0; i < 3; i++){
int start = max(0, day - costdays[i]);
cost = min(cost, dp[start] + costs[i]);
}
dp[day] = cost;
day++;
}
return dp[days.back()];
}
};
2020.5.7 另一个树的子树(简单)
思路:BFS,当遇到节点值和子树节点的值相同时,分别进行BFS
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSubtree(TreeNode* s, TreeNode* t) {
queue<TreeNode* > q;
q.push(s);
while(!q.empty()){
auto f=q.front();
q.pop();
if(f->val==t->val){
queue<TreeNode*> qf,qt;
qf.push(f);
qt.push(t);
bool equal=1;
while(!qt.empty() &&!qf.empty()){
auto tf=qf.front();
auto tt=qt.front();
//cout<val<<","<val<
qf.pop();
qt.pop();
if(tf->val!=tt->val)
{
equal=0;
break;
}
if(tt->left!=NULL)
qt.push(tt->left);
if(tt->right!=NULL)
qt.push(tt->right);
if(tf->left!=NULL)
qf.push(tf->left);
if(tf->right!=NULL)
qf.push(tf->right);
}
if(equal && qf.empty() && qt.empty())
return true;
}
if(f->left!=NULL)
q.push(f->left);
if(f->right!=NULL)
q.push(f->right);
}
return false;
}
};
2020.5.8 最大正方形(中等)
思路:动态规划
dp[i][j]表示以这个位置为右下角的正方形边长
假设当前格子的值是1,则最大长度为dp[i-1][j],dp[i][j-1],dp[i-1][j-1]这三个值中的最小值 +1
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if(matrix.size()==0 || matrix[0].size()==0)
return 0;
vector<vector<int>> dp(matrix.size(),vector<int>(matrix[0].size(),0));
for(int i=0;i<matrix.size();i++)
dp[i][0]=matrix[i][0]=='1';
for(int i=0;i<matrix[0].size();i++)
dp[0][i]=matrix[0][i]=='1';
int n=0;
for(int y=0;y<matrix.size();y++){
for(int x=0;x<matrix[0].size();x++){
if(matrix[y][x]=='0')
continue;
if(y==0 || x==0)
{
n=max(n,1);
continue;
}
dp[y][x]=min(min(dp[y-1][x],dp[y][x-1]),dp[y-1][x-1])+1;
n=max(n,dp[y][x]);
}
}
return n*n;
}
};
2020.5.9 x的平方根(简单)
思路:
递归,不断逼近左右边界
class Solution {
public:
int mySqrt(int x) {
if(x<=1)
return x;
return search(x,1,x);
}
int search(int x,int L,int R){
if(R-L<=1)
return L;
int Mid=L+ (R-L)/2;
if(Mid > x/Mid)
R=Mid;
else if(Mid < x/Mid)
L=Mid;
else
return Mid;
return search(x,L,R);
}
};
2020.5.10 二叉树的最近公共祖先(中等)
思路:遇到根节点相同的时候进行BFS比较
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> s;
stack<TreeNode*> ps, qs;
set<int> visited;
s.push(root);
while (!s.empty()){
auto n = s.top();
visited.insert(n->val);
if (n->val == p->val && ps.empty()){
ps = s;
}
if (n->val == q->val && qs.empty())
{
qs = s;
}
if(!qs.empty() && !ps.empty())
break;
if (n->left != NULL && visited.find(n->left->val)==visited.end())
{
s.push(n->left);
continue;
}
if (n->right != NULL && visited.find(n->right->val)==visited.end())
{
s.push(n->right);
continue;
}
s.pop();
}
while (ps.top()->val != qs.top()->val){
if (qs.size() > ps.size())
qs.pop();
else
ps.pop();
}
return qs.top();
}
};
2020.5.11 Pow(x,n) (中等)
思路:首先:x的n次幂
n可以很大,所以这题不能无脑的乘n次
然后对于负数:可以变成1/x的 -n次方
对于x的n次幂,同样还可转换成 x*x 的 n/2次幂,再根据奇偶乘以x即可,写成递归形式
class Solution {
public:
double myPow(double x, int n) {
if(n==INT_MIN)
return myPow(x*x,n/2);
if(n==1)
return x;
else if(n==0)
return 1;
else if(n<0)
return myPow(1.0/x,-n);
else{
if(n%2!=0)
return x*myPow(x*x,n/2);
else
return myPow(x*x,n/2);
}
}
};
2020.5.12 最小栈(简单)
思路:
双栈或双vector都可
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
}
vector<int> stack;
vector<int> mins;
void push(int x) {
stack.push_back(x);
if(mins.size()==0)
mins.push_back(x);
else
mins.push_back(min(x,mins.back()));
}
void pop() {
stack.pop_back();
mins.pop_back();
}
int top() {
return stack.back();
}
int getMin() {
return mins.back();
}
};
2020.5.13 二叉树的层序遍历(中等)
思路:BFS,存层数和节点
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL)
return res;
queue<pair<TreeNode*,int>> q;
q.push({root,0});
while(!q.empty()){
auto f=q.front();
q.pop();
if(res.size()>f.second)
res[f.second].push_back(f.first->val);
else
res.push_back(vector<int>(1,f.first->val));
if(f.first->left!=NULL)
q.push({f.first->left,f.second+1});
if(f.first->right!=NULL)
q.push({f.first->right,f.second+1});
}
return res;
}
};
2020.5.14 只出现一次的数字(简单)
思路:位运算,进行异或’^’,因为所有数字中只有一个出现了一次,其他都出现了两次,两次的和自身异或会变成0,而一次的不会,所以仅需要对所有数字进行异或即可
class Solution {
public:
int singleNumber(vector<int>& nums) {
int i=0;
for(int n:nums)
i^=n;
return i;
}
};
2020.5.15 和为K的子数组(中等)
思路:哈希表
和为K的子数组,L-R数组的和 可以通过从0-R 和 0-L相减得到
只需要统计从0到某个位置的和S,有多少个数组,及记录其索引,同时找到对应的值S+K,找出对应的索引,两两进行组合可得到和为K的子数组个数
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
vector<int> sum;
if(nums.size()<=0)
return 0;
if(nums.size()<=1)
return nums[0]==k;
sum.push_back(0);
map<int, vector<int>> sumMap;
for (int i : nums){
sum.push_back(sum.back() + i);
sumMap[sum.back()].push_back(sum.size() - 1);
}
sumMap[0].push_back(0);
int res = 0;
for (pair<int, vector<int>> p : sumMap){
int LSum = p.first;
int RSum = k + LSum;
if (sumMap.find(RSum) == sumMap.end())
continue;
int L = 0, R = 0;
//vector LIndex = sumMap[LSum], RIndex = sumMap[RSum];
while (L < sumMap[LSum].size() && R<sumMap[RSum].size()){
if (sumMap[LSum][L]>=sumMap[RSum][R])
{
R++;
if(k!=0)
continue;
}
res += sumMap[RSum].size() - R;
L++;
}
}
//sum(R)-sum(L)=Σ(L,R]
return res;
}
};
2020.5.16 K个一组翻转链表(困难)
思路:栈(或用长度为K+1的vector替代)
首个为上次的末尾节点/本次的头节点的上个节点
之后K个是待翻转的节点
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if(k<=1)
return head;
vector<ListNode*> Nodes(k+1,NULL);
ListNode* head0=new ListNode(0);
Nodes[0]=head0;
int index=1;
ListNode* cur=head;
while(cur!=NULL){
Nodes[index++]=cur;
cur=cur->next;
if(index>k){
ListNode* nextNode=cur;
ListNode* cur1=Nodes[--index];
while(index>0){
cur1->next=Nodes[--index];
if(index>0)
cur1=cur1->next;
}
cur1->next=nextNode;
Nodes[0]->next=Nodes[k];
index=1;
Nodes[0]=cur1;
}
}
return head0->next;
}
};
2020.5.17 课程表II(中等)
思路:
维护两个vector
一个是先决条件:
学习了预备课程后,可以使下个课程所需要的条件-1
另一个是课程需要多少门前置预备课程
每次从前置预备课程vecotr中找到为0的课程进行学习,同时对其影响到的其他课程进行前置条件数量-1
class Solution {
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<list<int>> prerequires(numCourses, list<int>());
vector<int> requireCnt(numCourses, 0);
for (int i = 0; i < prerequisites.size(); i++){
prerequires[prerequisites[i][1]].push_back(prerequisites[i][0]);
requireCnt[prerequisites[i][0]]++;
}
queue<int> q;
vector<int> res;
for (int i = 0; i < numCourses; i++)
{
if (requireCnt[i] == 0)
{
q.push(i);
}
}
if (q.empty())
return res;
while (!q.empty()){
int canlearned = q.front();
q.pop();
res.push_back(canlearned);
for (int i : prerequires[canlearned])
{
if (requireCnt[i] > 0){
requireCnt[i]--;
if (requireCnt[i] == 0){
q.push(i);
}
}
}
}
if (res.size() < numCourses)
return vector<int>();
return res;
}
};
2020.5.18 乘积最大子数组(中等)
思路:动态规划
维护二维vector,分别存储最大和最小值
class Solution {
public:
int maxProduct(vector<int>& nums) {
int dp[nums.size()+1][2];
dp[0][0]=1;
dp[0][1]=1;
int res=INT_MIN;
for(int i=1;i<=nums.size();i++){
int curNum=nums[i-1];
dp[i][0]=max(dp[i-1][0]*curNum,max(dp[i-1][1]*curNum,curNum));
dp[i][1]=min(dp[i-1][0]*curNum,min(dp[i-1][1]*curNum,curNum));
res=max(dp[i][0],res);
}
return res;
}
};
2020.5.19 验证回文字符串II(简单)
思路:双指针+递归
因为字符串最多删一个字符串,可以用一个bool表示
同时,双指针,一个最左一个最右,两个重合或者相遇时,则代表这个字符串回文串判断完成,遇到不同的时候,第一次可以删除,则返回删除左边或删除右边两个同时的递归,如果不可删除,则返回false
class Solution {
public:
bool candelete=1;
bool validPalindrome(string str) {
return validPalindrome(str,0,str.size()-1);
}
bool validPalindrome(string str,int L,int R){
while(L<R && str[L]==str[R]){
L++;
R--;
}
if(L>=R)
return 1;
if(!candelete)
return 0;
candelete=0;
return validPalindrome(str,L,R-1)||validPalindrome(str,L+1,R);
}
};
2020.5.20 每个元音包含偶数次的最长子字符串(中等)
思路:
aeiou总共有5个元音字母,可以使用一个char对其表示,其中1-5位分别表示对应字母的奇偶状态,为0时则为偶,为1时则为奇,对应的是按位进行异或操作
这个char总共有2^5(32)种状态,存储这每一种状态出现时的位置,下一次出现时,这两个位置之间的元音字符必定为偶数
class Solution {
public:
int findTheLongestSubstring(string s) {
int ans = 0, status = 0, n = s.length();
vector<int> pos(1 << 5, -1);
pos[0] = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == 'a') {
status ^= 1<<0;
} else if (s[i] == 'e') {
status ^= 1<<1;
} else if (s[i] == 'i') {
status ^= 1<<2;
} else if (s[i] == 'o') {
status ^= 1<<3;
} else if (s[i] == 'u') {
status ^= 1<<4;
}
if (~pos[status]) {
ans = max(ans, i + 1 - pos[status]);
} else {
pos[status] = i + 1;
}
}
return ans;
}
};
2020.5.21 最长回文子串(中等)
思路:从中间开始往两边扩张,看能达到最远的长度,分为两种:初始左右指针相同,初始左右指针差1
class Solution {
public:
string longestPalindrome(string s) {
string res="";
for(int i=0;i<s.size();i++){
int L=i,R=i;
while (L-1>=0 && R+1<s.size() && s[L-1]==s[R+1])
{
L--;
R++;
}
if(R-L+1>res.size())
{
res=s.substr(L,R-L+1);
}
if(i+1<s.size() && s[i]==s[i+1]){
L=i,R=i+1;
while(L-1>=0 && R+1<s.size() && s[L-1]==s[R+1]){
L--;
R++;
}
if(R-L+1>res.size()){
res=s.substr(L,R-L+1);
}
}
}
return res;
}
};
2020.5.22 从前序与中序遍历序列构造二叉树
思路:
前序:
根左右
中序:
左根右
即:
当我们拿到前序第一个时,可以确认根节点的值,可以在中序找到对应根节点的索引,而根节点左边全属于当前根节点的左部分,根节点的索引+1恰好反应了前序遍历种左部分的最后一个节点的位置,以此规律对序列进行拆分递归,最终还原得到二叉树
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() <= 0)
return NULL;
return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
}
TreeNode* build(vector<int>& pre, vector<int>& in, int preL, int preR, int inL, int inR) {
if (preL == preR)
return new TreeNode(pre[preL]);
TreeNode* root = new TreeNode(pre[preL]);
int nextinSplit = inL;
while (nextinSplit < inR && in[nextinSplit] != pre[preL])
nextinSplit++;
int LeftPart = nextinSplit - inL;
if (LeftPart)
root->left = build(pre, in, preL + 1, preL + LeftPart, inL, nextinSplit - 1);
int RightPart = inR - nextinSplit;
if (RightPart)
root->right = build(pre, in, preR - RightPart+1, preR, nextinSplit + 1, inR);
return root;
}
};
2020.5.23 最小覆盖子串(困难)
思路:用哈希表存储t中各字符出现的次数m,再用LR双指针进行滑动窗口
遍历s字符串,每一截的开始点为在t中出现过的字符的位置,当m中各字符对应的次数均小于等于0时,则说明此时包含字符串t,如果次数小于0,说明前面的可以匀一段到后面去,这时需对左指针进行检查
(比较繁琐的一题,但是按照很常规的想法就能做出来了)
class Solution {
public:
string minWindow(string s, string t) {
if (t == "")
return "";
map<char, int> m;
for (char c : t)
m[c]++;
int L = 0;
string res = "";
while (L < s.size() && m.find(s[L]) == m.end())
L++;
if (L >= s.size())
return "";
m[s[L]]--;
int R = L;
bool isContains = 0;
while (R < s.size()) {
isContains = 1;
for (pair<char, int> p : m) {
if (p.second > 0)
{
isContains = 0;
break;
}
}
//cout<
if (isContains) {
if (res=="" || res.size() > R - L + 1)
res = s.substr(L, R - L + 1);
m[s[L]]++;
L++;
while (L<=R && m.find(s[L]) == m.end())
L++;
continue;
}
else {
R++;
while (R < s.size() && m.find(s[R]) == m.end())
R++;
if (R >= s.size())
break;
m[s[R]]--;
}
}
return res;
}
};
2020.5.24 寻找两个正序数组的中位数(困难)
思路:直接优先队列
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
priority_queue<int> q;
for (int i : nums1)
q.push(i);
for (int i : nums2)
q.push(i);
int mid = (q.size()+1) / 2;
int size = q.size();
int cnt = 0;
double m;
while (cnt < mid)
{
m = q.top();
q.pop();
cnt++;
}
if (size % 2 == 0)
{
m += q.top();
m /= 2.0;
}
return m;
}
};
2020.5.25 LRU缓存机制(中等)
思路:需要插入顺序与存储顺序保持一致的map,这个c++里没提供,但是java里有个LinkedHashMap
当时赶着去打球,就没时间仔细想了,用的java版:
class LRUCache {
private LinkedHashMap<Integer, Integer> map;
int MaxCount;
int Count;
public LRUCache(int capacity) {
map = new LinkedHashMap<>();
MaxCount = capacity;
Count = 0;
}
public int get(int key) {
Integer value = map.remove(key);
if (value != null) {
map.put(key, value);
return value;
} else
return -1;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
map.remove(key);
map.put(key, value);
return;
}
map.put(key, value);
Count++;
if (Count > MaxCount) {
Count--;
Integer firstKey = (Integer) map.keySet().toArray()[0];
map.remove(firstKey);
}
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
c++中实现类似功能的方法:
用list链表加上value为迭代器的map
2020.5.26 寻找重复数(中等)
思路:用set或者数组也行,因为nums中数字是在1-n(n为数组长度)
只需要遍历一遍这个数组,统计某个数字出现的次数,次数不为0则找到重复数
下面贴的是懒方法
class Solution {
public:
int findDuplicate(vector<int>& nums) {
set<int> s;
for(int i:nums){
if(s.find(i)==s.end())
s.insert(i);
else
{
return i;
}
}
return 0;
}
};
2020.5.27 和可被K整除的子数组(中等)
思路:类似和为K的子数组
当前求和进行取模,能反映出有多少个位置,这些位置的和都满足与K取模等于a的条件,这些数之中两两可组成一个子数组,而子数组之间的和,必为可被K整除的数
class Solution {
public:
int subarraysDivByK(vector<int>& A, int K) {
unordered_map<int, int> record = {{0, 1}};
int sum = 0;
for (int elem: A) {
sum += elem;
// 注意 C++ 取模的特殊性,当被除数为负数时取模结果为负数,需要纠正
int modulus = (sum % K + K) % K;
++record[modulus];
}
int ans = 0;
for (auto [x, cx]: record) {
ans += cx * (cx - 1) / 2;
}
return ans;
}
};
2020.5.28 字符串解码(中等)
思路:双栈,一个存储栈顶的字符串,一个存储重复次数
class Solution {
public:
string decodeString(string s) {
stack<string> strstack;
stack<int> repeatstack;
string tmp = "",num = "";
for(int i=0;i<s.size();i++){
if(s[i]=='[')
{
strstack.push(tmp);
tmp="";
int repeatcnt=stoi(num);
num="";
repeatstack.push(repeatcnt);
}
else if(s[i]==']'){
string top=strstack.empty()?"":strstack.top();
strstack.pop();
int repeat = repeatstack.top();
repeatstack.pop();
string str="";
while(repeat > 0)
{
str += tmp;
repeat--;
}
tmp = top + str;
}
else if(isdigit(s[i])){
num += s[i];
}
else
{
tmp += s[i];
}
}
return tmp;
}
};
2020.5.29 打家劫舍(简单)
思路:动态规划,条件是跨过下一个值
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size() <= 0)
return 0;
if(nums.size() <= 1)
return nums[0];
if(nums.size() <= 2)
return max(nums[0], nums[1]);
if(nums.size() <= 3)
return max(nums[0] + nums[2], nums[1]);
vector<int> dp(nums.size(), 0);
dp[0] = nums[0];
dp[1] = nums[1];
dp[2] = nums[0] + nums[2];
for(int i = 3; i < dp.size(); i++)
{
dp[i] = max(dp[i], max(dp[i-2],dp[i-3]) + nums[i]);
}
return max(dp[nums.size() - 1], dp[nums.size() - 2]);
}
};
2020.5.30 柱状图中最大的矩形(困难)
思路:单调栈
对于每一个索引i,维护一个左指针,以及右指针,使其对于i是最低高度时的极限左右边界
维护单调栈:当栈顶对应的高度比当前i对应的高度满足大于等于的关系,不满足单调性时,确定i的左边界,同时弹出,直到栈空或者栈顶对应高度比当前对应高度小时
同时更新i的左索引并入栈
维护完成后,遍历每个位置,计算其左右边界总共的长度乘以当前的高度
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
vector<int> left(heights.size()), right(heights.size(), heights.size());
int res = 0;
stack<int> s;
for (int i = 0; i < heights.size(); i++){
while (!s.empty() && heights[s.top()] >= heights[i]){
right[s.top()] = i;
s.pop();
}
left[i] = s.empty() ? -1 : s.top();
s.push(i);
}
for (int i = 0; i < heights.size(); i++){
res = max(res, (right[i] - left[i] - 1)* heights[i]);
}
return res;
}
};
//之前用递归,java能过但是c++超时了。。
2020.5.31 对称二叉树(简单)
思路:两个BFS,但是要注意对称,即左部分的右部分与右部分的左部分相比较
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == NULL)
return 1;
if (root->left == NULL || root->right == NULL)
{
return root->left == NULL && root->right == NULL;
}
queue<TreeNode*> ql, qr;
ql.push(root->left);
qr.push(root->right);
while (!ql.empty() && !qr.empty()){
if (ql.size() != qr.size())
return 0;
TreeNode* l = ql.front();
TreeNode* r = qr.front();
ql.pop();
qr.pop();
if (l->val != r->val){
return 0;
}
if (l->left == NULL || r->right == NULL){
if (!(l->left == NULL && r->right == NULL))
{
return 0;
}
}
else{
ql.push(l->left);
qr.push(r->right);
}
if (l->right == NULL || r->left == NULL){
if (!(l->right == NULL && r->left == NULL)){
return 0;
}
}
else{
ql.push(l->right);
qr.push(r->left);
}
}
return 1;
}
};