贪心&BFS&DFS
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
思路:依次遍历,遇到所有数都累加,结果为负时,重新累加。用一个max实时记录子数组的最大值。
int maxSubArray(vector<int>& nums) {
int sum=0,max=INT_MIN;
for(auto i : nums){
sum+=i;
max=sum>max?sum:max;
if(sum<0) sum=0;
}
return max;
}
买卖股票的最佳时期
最多持有1股,买卖无数次
- DFS(暴力法) 时 间 复 杂 度 为 O ( N 2 ) 时间复杂度为 O(N^2) 时间复杂度为O(N2)
用0表示未持有股票,1表示持有股票。从昨天到今天,状态转换有3种,即
今天的行为 | 今天结束后的statr | 今天结束后的profit |
---|---|---|
今天什么也不做,或买入又卖出、卖出又买入 | 不变 | 不变 |
若持股,则卖出 | 1-0 | 加上今天的股价 |
若不持股,则买入 | 0-1 | 减去今天的股价 |
class Solution {
private:
int days;//记录最大天数
int max = 0;//记录最大收益
public:
int maxProfit(vector<int>& prices) {
days = prices.size();
//初始条件:昨天结束时也就是第一天开始时,未持股、利润为0
//再去列举第一天的三种行为,所以day = 0
DFS(prices,0,0,0);
return max;
}
//参数:day表示天数,第一天day=0,prices[day]即是今天的股价
void DFS(vector<int>& prices,int day,bool state,int profit){
if(day == days) {
//到最后一天了
max = profit>max?profit:max;
return;
}
//第day+1天有3种行为,每种行为都使得profit变化
DFS(prices,day+1,state,profit);
if(state){
DFS(prices,day+1,!state,profit+prices[day]);
}else{
DFS(prices,day+1,!state,profit-prices[day]);
}
}
};
- 贪心算法,O(N)
class Solution {
private:
int max = 0;//记录最大收益
public:
int maxProfit(vector<int>& prices) {
int days = prices.size();
for(int day = 0;day<days-1;day++){
int diff = prices[day+1]-prices[day];
if(diff > 0){
max += diff;
}
}
return max;
}
};
- 动态规划
挖坑
102二叉树层次遍历
/**
* Definition for a binary tree node.
*/
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {
}
};
- BFS。有迭代和递归的方式:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode *root)
{
vector<vector<int>> res;
if (!root)
return res;
queue<TreeNode *> queue;
TreeNode *node;
queue.push(root);
while (!queue.empty())
{
vector<int> level_res;
int size = queue.size();
for (int i = 0; i < size; i++)
{
node = queue.front();
queue.pop();
level_res.emplace_back(node->val);
if (node->left)
queue.push(node->left);
if (node->right)
queue.push(node->right);
}
res.push_back(level_res);
}
return res;
}
};
作者:24shi-01fen-_00_01
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/bfsdie-dai-di-gui-by-24shi-01fen-_00_01/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
helper(res,root,0);
return res;
}
void helper(vector<vector<int>>& res,TreeNode* node,int level){
if(!node) return ;
if(level>=res.size()){
vector<int> level_res;
res.emplace_back(level_res);
}
res[level].emplace_back(node->val);
if(node->left) helper(res,node->left,level+1);
if(node->right) helper(res,node->right,level+1);
}
};
作者:24shi-01fen-_00_01
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/bfsdie-dai-di-gui-by-24shi-01fen-_00_01/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
112二叉树路径之和
Given a binary tree and a sum, determine if the tree has a
root-to-leaf path such that adding up all the values along the path
equals the given sum.Note: A leaf is a node with no children.
- DFS:也有迭代和递归的方式。迭代的写法太麻烦了,写不出来。。
class Solution {
public:
bool hasPathSum(TreeNode *node,int sum){
if(!node) return false;
if(!node->left&&!node->right){
if(node->val == sum) return true;
}
return hasPathSum(node->left,sum-node->val)||hasPathSum(node->right,sum-node->val);
}
};
437. 路径总和 III@@
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
示例:
思路:视每个树节点为一个状态,每个状态后可以分出4个状态:
- 不要当前节点,遍历左孩子
- 不要当前节点,遍历右孩子
- 要当前节点,遍历左孩子
- 要当前节点,遍历右孩子
注意一旦选择要当前节点,也就是确定了开头节点,后续分岔状态则只剩下最后两种,因为是连续子序列。所以我加了个flag位记录是否确定好头节点,确定好有两种分岔,没确定好则有四种分岔。思想还是DFS。
class Solution {
public:
int ans = 0;
void helper(TreeNode* root, int sum,int flag){
if(root==NULL) return;
if(sum == root->val) {
ans++;
}
if(flag){
helper(root->left,sum-root->val,1);
helper(root->right,sum-root->val,1);
}else{
//这时候头节点没确定
helper(root->left,sum,0);
helper(root->right,sum,0);
//确定头节点了 此后只能连续
helper(root->left,sum-root->val,1);
helper(root->right,sum-root->val,1);
}
return;
}
int pathSum(TreeNode* root, int sum) {
helper(root,sum,0);
return ans;
}
};
速度还是有点慢的,是否哪里可以剪枝?
104二叉树最大/小深度
BFS
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode* > qu;
int size,len = 0;
int max = 0,min = 0;
int flag = 0;
if (!root) return 0;
qu.push(root);
TreeNode* node;
while(!qu.empty()){
len++;
size = qu.size();
for(int i =0;i<size;i++){
node = qu.front();
qu.pop();
if(node->left) qu.push(node->left);
if(node->right) qu.push(node->right);
if(!node->left&&!node->right){
if(!flag){
min = len;
flag = 1;
}
max = max>len?max:len;
}
}
}
return max;//min的时候可以提前return
}
};
求最大深度可用DFS递归写法
int maxDepth(TreeNode* root) {
if(!root) return 0;
return 1+max(maxDepth(root->left),maxDepth(root->right));
}
30 有效括号
Given n pairs of parentheses, write a function to generate all
combinations of well-formed parentheses.For example, given n = 3, a solution set is:
- 回溯法
class Solution {
public:
void backtrace(int left, int right, int n, string& s, vector<string>& res) {
//引用传递
if (left == n && right == n) {
res.push_back(s);
return;
}
if (left < n) {
s += "(";
backtrace(left + 1, right, n, s, res);
s.pop_back();
}
if (right < left) {
s += ")";
backtrace(left, right + 1, n, s, res);
s.pop_back();
}
}
vector<string> generateParenthesis(int n) {
vector<string> res;
string s;
backtrace(0, 0, n, s, res);
return res;
}
};
- DFS+剪枝
思路:在2n个格子中填入6个符号,有 2 2 n 2^{2n} 22n个填法。先看不剪枝的情况:
class Solution {
vector<string> ans;
int N;
public:
vector<string> generateParenthesis(int n) {
N = n;
DFS("");
for(auto i : ans){
cout<<i<<endl;
}
return ans;
}
void DFS(string s){
if(s.length() == 2*N){
ans.push_back(s);
return;
}
DFS(s+"(");
DFS(s+")");
}
};
输入3时,输出了 2 6 2^{6} 26种左右括号的排列组合,说明是对的。接下来通过left和right变量,表示已经使用的左右括号数目,增加两种停止递归的条件即可:
- 不可能先填入右括号(左括号数大于右括号数),
- 不可能有4个左括号(左括号数小于3)。
class Solution {
vector<string> ans;
int N;//类变量保存不同函数所需变量
public:
vector<string> generateParenthesis(int n) {
N = n;
DFS("",0,0);
return ans;
}
void DFS(string s,int left,int right){
if(left < right) return;
if(left > N) return;//剪掉了无用分支
if(s.length() == 2*N){
//符合题意的叶子节点,应保存到ans数组中
ans.push_back(s);
return;
}
DFS(s+"(",left+1,right);//string类可以直接加字符
DFS(s+")",left,right+1);
}
};