文章目录
- 1.二叉树的层次遍历Ⅱ
- ①广度优先,每次都在out最后插入(push_back()),然后最后将out反转reverse
- ②广度优先,每次都在out最前插入(out.insert(out.begin(),xxx))(本来以为会比方法一快很多,但实际确是比方法一慢很多)
- 2.将有序数组转换为二叉搜索树(构造不唯一的平衡二叉树)
- 二分法
- 3.平衡二叉树(判断平衡二叉树)
- 4.二叉树的最小深度(叶子节点的最小高度)
- ①递归(深度)
- ②迭代(广度)(遇到左右子树都为空的结点,返回它的高度)
- 5.路径总和
- ①递归:
- ②栈:
- 6.杨辉三角Ⅰ
- 7.杨辉三角Ⅱ
- 空间利用只有O(N),这个方法挺巧妙的
- 8.买股票的最佳时机Ⅰ
- 9.买股票的最佳时机Ⅱ
- ①贪心算法:
- ②动态规划:
- 10.买卖股票时机含冷冻期
- 动态规划
- 11.买卖股票的最佳时机含手续费
- 动态规划
1.二叉树的层次遍历Ⅱ
①广度优先,每次都在out最后插入(push_back()),然后最后将out反转reverse
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>>out;
queue<TreeNode*>q;
q.push(root);
while (!q.empty())
{
vector<int>inside;
int S = q.size();
for (int i = 0; i < S; i++)//把本层的弄出来
{
TreeNode* Node = q.front();
q.pop();
if (Node != NULL)
{
inside.push_back(Node->val);
q.push(Node->left);
q.push(Node->right);
}
}
if(inside.size()>0)
out.push_back(inside);
}
reverse(out.begin(),out.end());
return out;
}
②广度优先,每次都在out最前插入(out.insert(out.begin(),xxx))(本来以为会比方法一快很多,但实际确是比方法一慢很多)
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>>out;
queue<TreeNode*>q;
q.push(root);
while (!q.empty())
{
vector<int>inside;
int S = q.size();
for (int i = 0; i < S; i++)//把本层的弄出来
{
TreeNode* Node = q.front();
q.pop();
if (Node != NULL)
{
inside.push_back(Node->val);
q.push(Node->left);
q.push(Node->right);
}
}
if(inside.size()>0)
out.insert(out.begin(),inside);
}
return out;
}
上面两个是方法二,最下面的是方法一
2.将有序数组转换为二叉搜索树(构造不唯一的平衡二叉树)
因为是有序的数组,排序刚好是二叉树的中序遍历的结果,所以想到用二分法
二分法
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
TreeNode* dfs(vector<int>& nums,int LE,int RI)
{
if (LE >= RI)
return NULL;
int mid = LE + (RI - LE) / 2;
TreeNode *root = new TreeNode(nums[mid]);
root->left = dfs(nums, LE, mid);
root->right = dfs(nums, mid+1, RI);//必须要+1不然最后两个的时候会出错没办法取完了
return root;//在内层的时候是返回一颗一颗子树(从最底层的子树开始倒回去返),在最外层是返回最终答案
}
TreeNode* sortedArrayToBST(vector<int>& nums)
{
if (nums.size() == 0)
return NULL;
if (nums.size() == 1)
return new TreeNode(nums[0]);
int RI = nums.size(),LE=0;
return dfs(nums, LE, RI);
}
3.平衡二叉树(判断平衡二叉树)
用两个递归,一个递归是算结点深度,一个递归是判断结点左右子树是否平衡即深度差<=1
int depth(TreeNode *Node)
{
if (Node == NULL)
return 0;
else
return max(depth(Node->left),depth(Node->right)) + 1;//深度计算,和104题是一样的
}
bool isBalanced(TreeNode* root)
{
if (root == NULL)
return true;
int gap = abs(depth(root->left) - depth(root->right));//左右子树深度差
return gap <= 1 && isBalanced(root->left) && isBalanced(root->right);//左右子树深度差平衡,并且子树的子树...也平衡
}
4.二叉树的最小深度(叶子节点的最小高度)
①递归(深度)
如果根空,返回0
如果左右有一个空或者都空 ,返回LE+RI+1,因为空的话是0
如果都不空,返回LE和RI的最小值+1
int minDepth(TreeNode* root)
{
if (root == NULL)//根空
return 0;
int LE = minDepth(root->left);
int RI = minDepth(root->right);
if (root->left == NULL || root->right == NULL)//左右有一个空或者都空
return LE+RI+1;
return min(LE, RI)+1;//左右都不空
}
int main()
{
TreeNode *tree = new TreeNode(3);
tree->left = new TreeNode(9);
tree->right = new TreeNode(20);
tree->right->left = new TreeNode(15);
tree->right->right = new TreeNode(7);
cout << minDepth(tree);
return 0;
}
②迭代(广度)(遇到左右子树都为空的结点,返回它的高度)
int minDepth(TreeNode* root)
{
if (root == NULL)
return 0;
queue<TreeNode*>q;
q.push(root);
int ans = 1;
while (!q.empty())
{
int Size = q.size();
for (int i = 0; i < Size; i++)
{
TreeNode *cur = q.front();
q.pop();
if (cur->left == NULL && cur->right == NULL)
return ans;
if (cur->left != NULL)//只插不空的子树
q.push(cur->left);
if (cur->right != NULL)
q.push(cur->right);
}
ans++;
}
return 0;
}
或者
int minDepth(TreeNode* root)
{
if (root == NULL)
return 0;
queue<TreeNode*>q;
q.push(root);
int ans = 1;
while (!q.empty())
{
int Size = q.size();
for (int i = 0; i < Size; i++)
{
TreeNode *cur = q.front();
q.pop();
if (cur != NULL)
{
if (cur->left == NULL && cur->right == NULL)
return ans;
q.push(cur->left);//子树空不空都插
q.push(cur->right);
}
}
ans++;
}
return 0;
}
5.路径总和
①递归:
这两个是一个意思
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL)
return false;
if (hasPathSum(root->left, sum - root->val))
return true;
if (hasPathSum(root->right, sum - root->val))
return true;
if (root->left == NULL && root->right == NULL && sum == root->val)
return true;
return false;
}
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL)
return false;
if (root->left == NULL && root->right == NULL && sum == root->val)
return true;
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
②栈:
//使用两个栈,一个栈保存节点一个栈保存遍历过的节点的总和
bool hasPathSum(TreeNode* root, int sum) {
if(!root) return false;//空
stack<TreeNode*> path;//点
stack<int> sub;//和
path.push(root);
sub.push(root->val);
while(!path.empty()){
TreeNode* pnode = path.top();
path.pop();
int pval = sub.top();
sub.pop();
if(!pnode->left && !pnode->right && pval==sum) return true;
if(pnode->left){
path.push(pnode->left);
sub.push(pval+pnode->left->val);
}
if(pnode->right){
path.push(pnode->right);
sub.push(pval+pnode->right->val);
}
}
return false;
}
6.杨辉三角Ⅰ
方法一:就是逐层添进去
vector<vector<int>> generate(int numRows)
{
vector<vector<int>>out;
if(numRows==0)
{
out={};
return out;
}
vector<int>inside;
inside.push_back(1);
out.push_back(inside);
for (int i = 2; i <= numRows; i++)
{
vector<int>inside;
inside.push_back(1);
for (int j = 2; j <= i - 1; j++)
{
inside.push_back(out[i - 2][j - 2] + out[i - 2][j - 1]);
}
inside.push_back(1);
out.push_back(inside);
}
return out;
}
方法二:
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> out;
for(int i=0;i<numRows;i++)
out.push_back(vector<int>(i+1,1));//看这里,vector可以不起名字的,size是i+1位,每位初始值是1
for(int i=0;i<numRows;i++)
for (int j = 1; j < i; j++)
out[i][j]=out[i-1][j-1]+out[i-1][j];
return out;
}
};
7.杨辉三角Ⅱ
空间利用只有O(N),这个方法挺巧妙的
vector<int> getRow(int rowIndex) {
vector<int>ans(rowIndex+1, 1);//rowIndex+1位,初值为1
for (int i = 2; i <= rowIndex; i++)
{
for (int j = i - 1; j > 0; j--)
{
ans[j] += ans[j - 1];//看看图就懂了,从上层往下层推,每层内部从右往左推
}
}
return ans;
}
8.买股票的最佳时机Ⅰ
int maxProfit(vector<int>& prices) {
int max_gap = 0;
int minprice = INT_MAX;
for (int i = 0; i < prices.size(); i++)
{
max_gap = max(max_gap, prices[i] - minprice);
minprice = min(minprice, prices[i]);
}
return max_gap;
}
9.买股票的最佳时机Ⅱ
①贪心算法:
逢低买入,逢高卖出,前一天低于后一天价格就符合(画个折线图就懂了)
int maxProfit(vector<int>& prices) {
int ans=0;
for (int i = 1; i < prices.size(); i++)
{
if (prices[i] > prices[i - 1])
ans += prices[i] - prices[i - 1];
}
return ans;
}
②动态规划:
dp[ i ] [ 0 ]:状态表示在 第i位的时候 不持有,dp的值 是该状态下 最大持有的钱
dp[ i ] [ 1 ]:状态表示在 第i位的时候 持有,dp的值 是该状态下 最大持有的钱
int maxProfit(vector<int>& prices) {
int dp[prices.size()][2];//0不买或者现在卖(不持有),1买(持有)
dp[0][0] = 0;
dp[0][1] = -prices[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]);//左边是持有的时候继续保持,右边是不持有的时候买进
}
return dp[prices.size()-1][0];
}
ps:visual studio的编译器不是GCC,所以int dp[prices.size()][2];定义这个的时候是会报错的,只有GCC才支持不是常量定义数组长度。
10.买卖股票时机含冷冻期
动态规划
和上面122题的区别在于循环里的 dp [ i-2 ] [ 0 ]上一次卖出隔一天
int maxProfit(vector<int>& prices) {
if (prices.size() == 0)
return 0;
int dp[prices.size()][2];//0不买或者现在卖(不持有),1买(持有)
dp[0][0] = 0;
dp[0][1] = -prices[0];
if(prices.size()>1)//i等于1的时候dp[1][1]是-price[0]和-prices[1]看哪个的负值比较大
{
dp[1][0] = max(dp[0][0], dp[0][1] + prices[1]);
dp[1][1] = max(-prices[0], -prices[1]);
}
for (int i = 2; i < prices.size(); i++)/因为有冻结期,所以i要大于2
{
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);//左边是不持有的时候继续保持,右边持有的时候是卖出去
dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i]);//左边是持有的时候继续保持,右边是不持有的时候买进
}
return dp[prices.size()-1][0];
}
11.买卖股票的最佳时机含手续费
动态规划
和上上面122题的区别在于循环里卖出的时候要减去手续费fee
int maxProfit(vector<int>& prices, int fee) {
if (prices.size() == 0)
return 0;
int dp[prices.size()][2];//0不买或者现在卖(不持有),1买(持有)
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < prices.size(); i++)
{
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[prices.size() - 1][0];
}