【算法】贪心&BFS&DFS

贪心&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);
    }
};

猜你喜欢

转载自blog.csdn.net/Protocols7/article/details/104540640