一 k个元素一组反转链表
前奏:反转链表
搞3个指针即可,分别指向当前处理的节点,当前处理节点的前一个节点,当前处理节点的下一个节点。
下一节点在进入循环之前已经设置了,不是空的。
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead==NULL)
return NULL;
ListNode *pre=NULL;
ListNode *current=pHead;
ListNode *_next=current->next;
while(_next)
{
current->next=pre;
pre=current;
current=_next;
_next=current->next;
}
current->next=pre;
return current;
}
};
下一节点在进入循环前设置为空,每次进入循环之后才设置为当前节点的下一节点。
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead==NULL)
return NULL;
ListNode *pre=NULL;
ListNode *current=pHead;
ListNode *_next=NULL;
while(current)
{
_next=current->next;
current->next=pre;
pre=current;
current=_next;
}
return pre;
}
};
2种方法都可以,但要特别注意循环结束的条件和最后返回的指针,否则会处出现_next指向空指针的下一个节点或者返回空指针。
K个元素为一组反转链表,不够K个元素不反转
每K个元素按照反转链表的方式进行反转,然后要把这些链表链接起来即可。
1 2 3 4 5 6 7 8
以每隔3个元素反转
3 2 1 6 5 4 7 8
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if(k<=1||head==NULL)
return head;
ListNode *pre=NULL;
ListNode *current=head;
ListNode *_next=NULL;
ListNode *check=head;
int cnt=0;
int cando=0;
//判断够不够反转
while(check&&cando<k)
{
cando++;
check=check->next;
}
//如果个数到达K,说明可以反转
if(cando==k)
{
while(cnt<k)
{
_next=current->next;
current->next=pre;
pre=current;
current=_next;
cnt++;
}
//这个时候,current和_next都指向下一个分段的开始
if(current)
{
head->next=reverseKGroup(current,k);
}
//不要忘了这个
return pre;
}
//否则,不可以反转,直接返回原来分段头节点即可
else
return head;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if(k<=1||!head||!head->next)
return head;
ListNode *pre=NULL;
ListNode *cur=head;
ListNode *next=NULL;
ListNode *check=head;
int count=0;
int checkcount=0;
while(checkcount<k&&check)
{
checkcount++;
check=check->next;
}
if(checkcount==k)
{
while(count<k)
{
next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
count++;
}
if(next)
{
head->next=reverseKGroup(next,k);
}
return pre;
}
else
return head;
}
};
、题目链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/
二 股票买卖问题
股票买卖时机一
法一 动态规划思路
前i天的最大利润=max(前i-1天的最大利润,第i天股票的价值-前i天股票的最小值)
class Solution {
public:
int solve(vector<int> &prices)
{
int res[prices.size()+1];
memset(res,0,sizeof(res));
int min_data=INT_MIN;
int i;
for(i=1;i<=prices.size();i++)
{
if(prices[i-1]<min_data)
min_data=prices[i-1];
res[i]=max(res[i-1],prices[i-1]-min_data);
}
return res[i-1];
}
int maxProfit(vector<int>& prices) {
if(prices.size()<=1)
return 0;
else if(prices.size()==2&&prices[0]>=prices[1])
return 0;
else if(prices.size()==2&&prices[0]<prices[1])
return prices[1]-prices[0];
else
return solve(prices);
}
};
法二
维系2个变量,分别是利润最大值和股票价值的最小值(初始为第一支股票的价值)
之后遍历数组,(1)如果当前价值小于股票最小值,更新股票最小值
(2)否则即当前价值大于等于最小价值,则用当前值减去股票最小值与利润最大值做比较,判断是否更新即可。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()<=1)
return 0;
int max_data=0;
int min_data=prices[0];
for(int i=1;i<prices.size();i++)
{
if(prices[i]<min_data)
min_data=prices[i];
else if(prices[i]-min_data>max_data)
max_data=prices[i]-min_data;
}
return max_data;
}
};
股票买卖时机二
这个题目真的。。
[7, 1, 5, 6]
第二天买入,第四天卖出,收益最大(6-1),所以一般人可能会想,怎么判断不是第三天就卖出了呢? 这里就把问题复杂化了,根据题目的意思,当天卖出以后,当天还可以买入,所以其实可以第三天卖出,第三天买入,第四天又卖出((5-1)+ (6-5) === 6 - 1)。所以算法可以直接简化为只要今天比昨天大,就卖出。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()<=1)
return 0;
int res=0;
int before=prices[0];
for(int i=1;i<prices.size();i++)
{
if(prices[i]>before)
res+=prices[i]-before;
before=prices[i];
}
return res;
}
};
股票买卖时机三
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()<=0)
return 0;
int release1=0;
int release2=0;
int hold1=INT_MIN;
int hold2=hold1;
for(int i=0;i<prices.size();i++)
{
int p=prices[i];
hold1=max(hold1,-p);
release1=max(release1,hold1+p);
hold2=max(hold2,release1-p);
release2=max(release2,hold2+p);
}
return release2;
}
};
思路就是维护4个变量,hold1,release1,hold2,release2来表示第一次买入股票/第一次出售股票/第二次买入股票/第二次卖出股票的收益最大值。遍历数组时,把该只股票当作4种情况都走一遍,然后release2就是最后的结果。
股票买卖的最佳时机四
限制k次交易
当k>=数组长度/2时,因为交易最频繁的情况时最多有数组长度/2次交易,比如3 5 2 7 4 9 2 6,即买进卖出交替进行。这是可以采取贪心算法,即买卖时机二的问题。
对于其它的k,可以采用买卖时机三的方法
class Solution {
public:
int greedy(vector<int> &vec)
{
int before=vec[0];
int res=0;
for(int i=1;i<vec.size();i++)
{
if(vec[i]>before)
res+=vec[i]-before;
before=vec[i];
}
return res;
}
int maxProfit(int k, vector<int>& prices) {
if(prices.size()<=1||k<=0)
return 0;
if(k>=prices.size()/2)
return greedy(prices);
//a[i][0] a[i][1]分别表示第i笔交易买入或卖出的最大利润
int a[k][2];
memset(a,0,sizeof(a));
for(int i=0;i<k;i++)
{
a[i][0]=INT_MIN;
}
for(int i=0;i<prices.size();i++)
{
a[0][0]=max(a[0][0],-prices[i]);
a[0][1]=max(a[0][1],a[0][0]+prices[i]);
for(int j=1;j<k;j++)
{
a[j][0]=max(a[j][0],a[j-1][1]-prices[i]);
a[j][1]=max(a[j][1],a[j][0]+prices[i]);
}
}
return a[k-1][1];
}
};
股票买卖的最佳时机(含有冷冻期)309
二维数组法
dp[k][3]
dp[k][0]表示现在手中有股票的最大利润
dp[k][1]表示现在(今天)刚卖出股票的最大利润
dp[k][2]表示今天处于冷冻期的最大利润或者虽然目前没有持有股票但是今天仍不买入(可以看作冷冻期)的最大利润
初始化
dp[0][0]=-prices[0] //买入第一天的股票才保证手里有股票
dp[0][1]=0 //之前没有股票,所以是0
dp[0][2]=0 //今天处于冷冻期,但之前没有交易,所以为0
状态转移方程
dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i]);
//分别表示前一天已经有股票,前一天处于冷冻期今天买进
dp[i][1]=dp[i-1][0]+prices[i];
//只有一种情况就是前一天持有股票,今天卖出
dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
//分别表示前一天卖出或前一天依然处于冷冻期内
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()<=1)
return 0;
int dp[prices.size()][3];
dp[0][0]=-prices[0];
dp[0][1]=0;
dp[0][2]=0;
for(int i=1;i<prices.size();i++)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i]);
dp[i][1]=dp[i-1][0]+prices[i];
dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
}
return max(dp[prices.size()-1][1],dp[prices.size()-1][2]);
}
};
股票买卖的最佳时机(含有手续费)714
二维数组法(动态规划法)
二维数组:dp[k][2]
其中dp[k][0]表示在第k天持有股票的最大利润。
dp[k][1]表示在第k天不持有股票的最大利润。
状态转移方程如下:
dp[k][0]=max(dp[k-1][0],dp[k-1][1]-prices[k]);
//dp[k-1][0]表示第k-1天持有股票
//dp[k-1][1]表示第k-1天没有持有股票,需要在第k天买进
dp[k][1]=max(dp[k-1][1],dp[k-1][0]+prices[k]-fee);
//dp[k-1][1]表示第k-1天不持有股票
//dp[k-1][0]表示第k-1天持有股票,需要在第k天卖出股票
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
if(prices.size()<=1)
return 0;
int dp[prices.size()][2];
dp[0][0]=-prices[0];
dp[0][1]=0;
for(int i=1;i<prices.size();i++)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]-fee);
}
return dp[prices.size()-1][1];
}
};
改进法
hold表示当天买进或不买进的最大利润
release表示当天卖出或不卖出的最大利润,卖出之前需要先买。买进来要先卖。
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
if(prices.size()<=1)
return 0;
int hold=INT_MIN;
int release=0;
for(int i=0;i<prices.size();i++)
{
hold=max(hold,release-prices[i]);
release=max(release,prices[i]+hold-fee);
}
return release;
}
};
三 接雨水
法一
遍历数组,找出柱子高度的最大值。
然后从2端分别开始遍历直至到达柱子高度最大值为止。
比如,从首部开始遍历时,找到目前(局部)最大值,如果之后遍历的值比这个局部最大值小,就说明会填充雨水,填充的值就是局部最大值减去当前值。
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()==0||height.size()==1||height.size()==2)
return 0;
int res=0;
//找出最大值的下标
int max_id=0;
for(int i=1;i<height.size();i++)
{
if(height[i]>height[max_id])
max_id=i;
}
//从首部开始遍历
int max_par=0;
for(int i=0;i<max_id;i++)
{
if(height[i]>max_par)
max_par=height[i];
else
res+=max_par-height[i];
}
//从尾部开始遍历
max_par=0;
for(int i=height.size()-1;i>max_id;i--)
{
if(height[i]>max_par)
max_par=height[i];
else
res+=max_par-height[i];
}
return res;
}
};
法二
使用2个数组分别保存数组下标i之前的最大高度,i之后的最大高度
然后开始遍历数组,得出i之前的最大高度和i之后最大高度的最小值,以这个作为统一高度,如果该高度减去下标i对应的高度为负,结果不变,为正,结果更新即可。
class Solution {
public:
int trap(vector<int>& height) {
if(height.size()==0||height.size()==1||height.size()==2)
return 0;
vector<int> left(height.size());
vector<int> right(height.size());
int res=0;
for(int i=1;i<height.size();i++)
{
left[i]=max(height[i-1],left[i-1]);
}
for(int i=height.size()-2;i>=0;i--)
{
right[i]=max(height[i+1],right[i+1]);
}
for(int i=0;i<height.size();i++)
{
int num=min(left[i],right[i]);
res+=max(0,num-height[i]);
}
return res;
}
};