Leetcode 刷题经验集

==========================================

1.树

剑指68 二叉搜索树的最近公共祖先

如果一个root是pq的公共祖先,要么,pq在root的两侧,要么,p或者q是root,另外一个是其左右子树中的节点。利用二叉树的性质,小于根节点的都在根节点的左侧,大于根节点的都在根节点的右侧。他们的公共祖先应该就在从根节点到他们的第一个大于a小于b的分叉节点。

剑指68-2 二叉树的最近公共祖先

考虑对二叉树后序遍历,搜索pq节点,找到后返回,自底向上回溯。

当一个节点第一次同时左右子树都找到pq两个节点的时候,说明他就是最近公共祖先,我们对其返回。

当前一个root左子树有p或者q,而右子树没有,要么,pq均在左子树,这个左子树节点就是作为公共祖先返回,要么,pq在最近公共祖先的两侧,那么返回该节子点作为指示该子树包含一个节点。

如果root两个子树搜都没搜到,说明不可能为祖先,返回空节点

==========================================

2、链表

==========================================

3、买卖股票

121. 买卖股票的最佳时机

Dp 思想, 一维 每天有两种状态,持有股票 buy 不持有股票 sell。初始化buy为负无穷,sell为卖出,设为0。每天判断是否是最低值,是否有必要买入。然后判断今天卖出的利润是不是比之前的利润高,如果是卖出。

 

int buy = INT_MIN, sell = 0;

for (const int & x : prices)

{

buy = max(buy, -x);

sell = max(sell, buy + x);

}

return sell;

}

122. 买卖股票的最佳时机 II

你可以尽可能地完成更多的交易(多次买卖一支股票)

1.贪心算法,一次遍历,只要当前股票价值递增就保留计算利润,只要股票价格降低就重新买入。

2、dp, 二维dp,一个表示持有股票的收益,一个表示不持有股票的收益。当天持有股票的收益为max(昨天不持有股票的收益减去今天买股票的钱,昨天持有股票今天不做变动)。当天不持有股票的收益为max(昨天持有股票今天卖出的收益,昨天不持有股票的收益)

3、dp 只有i与i-1之间的状态转换,只需要两个变量就能实现 buy sell。buy初始化为INTMIN,初始时没有收益。sell为卖出股票 ,收益为零。 第i天开始的时候,要根据股价涨跌判断是买入还是卖出,如果价格跌了,考虑昨天卖出,今天买入。如果价格涨了,考虑昨天持有,今天卖出的利润

 

int buy = INT_MIN, sell = 0;

for (const int & x : prices)

{

buy = max(buy, sell - x);//第i天,如果价格跌了,考虑昨天卖出,今天买入

sell = max(sell, buy + x);//如果价格涨了,考虑昨天持有,今天卖出的利润

}

123. 买卖股票的最佳时机 III

你最多可以完成 两笔 交易。

DP 思路和上一题一样, 考虑状态转移方程,在第i天,如果是第一次买入股票,我们可以选择买也可以选择不买,有buy1 = max( buy1‘’, -prices[i]),buy1’是上一天的。如果是对于第一次卖股票操作而言,我们可以不卖,也可以卖了赚钱,有sell1 = max(sell1, buy1 + prices[i])。考虑到当天买入卖出收益为零,可以直接使用buy1。buy2和sell2是第二次买卖的操作,和第一次意义一样。

然后考虑边界条件。 初始时,没有收入,将所有持有设为负无穷。不持有为0

 

for (const int & x : prices)

{

buy1 = max(buy1, -x);

sell1 = max(sell1, buy1 + x);

buy2 = max(buy2, sell1 - x);

sell2 = max(sell2, buy2 + x);

}

188. 买卖股票的最佳时机 IV

你最多可以完成 k 交易。

DP 状态 dp[天数][持有与否][第k次卖出] , 考虑状态转移方程,

dp[i][1][k] = max( dp[i-1][1][k], dp[i - 1][0][k-1] - price[i])

dp[i][0][k] = max( dp[i-1][0][k], dp[i - 1][1][k] + price[i])

实际上只有i和i-1的转化,可以降到二维。dp[2][k]

考虑边界条件, 初始时,没有收入,将所有持有收益设为负无穷。不持有为0

 

for (int i = 0; i < size; i++)

{

dp[1][0] = max(dp[1][0], -prices[i]);

dp[0][0] = max(dp[0][0], dp[1][0] + prices[i]);

for (int j = 1; j < k; j++)

{

dp[1][j] = max(dp[0][j-1] - prices[i], dp[1][j]);

dp[0][j] = max(dp[1][j] + prices[i], dp[0][j]);

}

}

309. 最佳买卖股票时机含冷冻期

你可以尽可能多的完成交易

DP 考虑状态方程 dp【天数】【买入、卖出、冷冻期】。

考虑第i天买入,要么不买,要么只能在至少今天不是冷冻期买,dp[i][0] = max(dp[i -1][0], dp[i-1][2] - prices[i])。

对于卖出,计算在之前持有股票的基础之上卖出,dp[i][1] = dp[i-1] + prices[i]。

对于冷冻期,如果昨天没做操作,那么收益不变,如果昨天卖出了,那么今天就是冷冻期。收益为dp[i][2] = max(dp[i-1][1], dp[i -1][2])。

可以看的出来状态仅仅与昨天今天有关,只需要维护昨天今天收入卖出冷冻六个变量值即可

 

int buy = INT_MIN, sell0 = 0, sell1 = 0;

int nbuy, nsell0, nsell1;

for (const auto & x : prices)

{

nbuy = max(buy, sell1 - x);

nsell0 = buy + x;

nsell1 = max(sell0, sell1);

buy = nbuy;

sell0 = nsell0;

sell1 = nsell1;

}

return max(sell0, sell1);

714. 买卖股票的最佳时机含手续费

非负整数 fee 代表了交易股票的手续费用。你可以无限次地完成交易

Dp 还是无限买卖的思路,只是卖的时候价格手续费

 

int buy = INT_MIN + fee, sell = 0; // +fee防止溢出

for (const auto & x : prices)

{

buy = max(buy, sell - x);

sell = max(sell, buy + x - fee);

}

==========================================

4、排序数组

153. 寻找旋转排序数组中的最小值

单调递增数组的旋转。

这个题目的主要思路是用二分法,不断地逼近最小值。

一般而言可以分为两种情况,原数组较大一部分在旋转后的左侧,另一种是在数组右侧。

由此我们可以以数组的中间值进行判断,

当中间值小于数组右侧的值时,说明在当前部分,数组是递增的,最小值应该在包括中间值的左侧

当中间值大于数组右侧的值时,说明右侧发生了旋转,最小值应该在不包括中间值的右侧。

154. 寻找旋转排序数组中的最小值 II

这里有两种方式,

一种和上面的方法一样,不过当中间值等于右侧值的时候,可以想到,最小值可能在中间值的左侧也可能在中间值的右侧。但是既然右边界和中间值相等,必然不会是右边界,所以我们可以缩小右边界。

一种是排除重复元素的,不考虑相等的事了。

剑指03数组中的重复的数字

如果是空间要求O(1)的话可以使用一种原地hash 的。

因为题目中给的元素是0 - n-1的,我们可以想到将元素i放到数组的位置i,

将他放到正确的位置,但是如果这两个地方的元素相等时,说明发生了重复。

==========================================

5、矩阵遍历

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false

其核心思想是遍历矩阵,对于矩阵中的每一个节点,进行一次dfs,如果当前节点未访问,且当前char和word匹配,设置当前节点为已经访问。然后右下两个方向再次dfs。当最后一个word匹配成功的时候返回1。

一般的思路是用visited,这里可以使用一个‘\0’暂时替换对应的元素,使用完成后再赋给其原来的值。

 

for(int i = 0; i < r ; i++)

for(int j = 0 ; j < c ;j++)

if(helper(board, i, j, word, 0))

==========================================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值