leetcode每日一题39

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

  1. 确定dp数组(dp table)以及下标的含义
    dp[i][j] j=0 不持有股票
    j=1持有股票
    i:第i天
    dp[i][j]:第i天,持有状态为j时的最大现金
  2. 确定递推公式
    dp[i][0]=max(dp[i-1][0], dp[i-1][1]+prices[i]);
    第i天不持有股票,那可以是前一天持有股票然后卖出去了,和前一天就不持有股票
    dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i];
    第i天持有股票,可以是前一天就持有股票,或者前一天不持有,今天买入
  3. dp数组如何初始化
    dp[0][0]=0;
    dp[0][1]=-prices[0];
  4. 确定遍历顺序
    从左到右,没啥好说的
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]);
}
  1. 举例推导dp数组

最后代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(),vector<int>(2,0));
        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];
    }
};

93.复原ip地址

相当于分割字符串,而且要返回的是所有的分割方法
那就是回溯了
分割问题可以使用回溯搜索法把所有可能性搜出来

  1. 确定递归参数
    需要一个全局变量的vector保存所有的回溯结果
    需要字符串s
    因为不能重复分割,所以需要一个startIndex
    还需要一个记录当前是第几个分割点的pointNum,因为只能分割成4段
    不需要递归结束后计算什么,所以不需要返回值
vector<string> result;
void backtracking(string& s, int startIndex,int pointNum)
  1. 确定递归终止条件
    一共只能切成四段,所以pointNum==3的时候,就会终止递归。不过此时还得判断一下最后一段是否合法,合法才能加进结果集
if(pointNum==3)
{
	if(isValid(s,startIndex,s.size()-1))
		result.push_back(s);
}
  1. 单层搜索的逻辑
    判断[startIndex, i]是否合法,合法就加上’.‘,分割该字串,然后递归下一层,因为字符串加上了一个’.',pointNum++,所以下一个递归的startIndex应该从i+2开始。不合法就结束本层
for(int i=startIndex;i<s.size();i++)
{
	if(isValid(s,startIndex,i)){
		s.insert(s.begin()+i+1,'.');
		pointNum++;
		backtracking(s,i+2,pointNum);
		pointNum--;
		s.erase(s.begin()+i+1);
}else break;

接下来写一下如何判断字符串合法
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0)
那就是s[start]=='0’且start!=end,false
s[i] > ‘9’ || s[i] < ‘0’ false
num>255 false

bool isValid(string s,int start, int end)
{
	if(s[start]=='0'&& start != end)
		return false;
	int num=0;
	for(int i=start;i<=end;i++){
		if(s[i] > '9' || s[i] < '0')
			return false;
		num = num * 10 + (s[i] - '0');
        if (num > 255) { // 如果大于255了不合法
            return false;
        }
    }
    return true;
}

不过还存在另一种不合法的情况
当s="101023"时,我的代码输出了[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“10.10.23.”,“10.102.3.”,“101.0.2.3”,“101.0.23.”]
而预期结果是[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“10.10.23.”,“10.102.3.”,“101.0.2.3”,“101.0.23.”]
可以看到我的代码在字符串结尾加了分割
这是因为回溯的时候,i+2有可能大于s.size()
所以要判定一下start和end的大小

bool isValid(string s,int start, int end)
{
	if(start>end)
		return false;
	if(s[start]=='0'&& start != end)
		return false;
	int num=0;
	for(int i=start;i<=end;i++){
		if(s[i] > '9' || s[i] < '0')
			return false;
		num = num * 10 + (s[i] - '0');
        if (num > 255) { // 如果大于255了不合法
            return false;
        }
    }
    return true;
}

整体代码:

class Solution {
public:
    bool isValid(string s,int start, int end)
    {
        if (start > end) {
            return false;
        }
        if(s[start]=='0'&& start != end)
            return false;
        int num=0;
        for(int i=start;i<=end;i++){
            if(s[i] > '9' || s[i] < '0')
                return false;
            num = num * 10 + (s[i] - '0');
            if (num > 255) { // 如果大于255了不合法
                return false;
            }
        }
        return true;
    }
    vector<string> result;
    void backtracking(string& s, int startIndex, int pointNum) {
        if (pointNum == 3) {
            if (isValid(s, startIndex, s.size() - 1))
                result.push_back(s);
            return;
        }

        for (int i = startIndex; i < s.size(); i++) {
            if (isValid(s, startIndex, i)) {
                s.insert(s.begin() + i + 1, '.');
                pointNum++;
                backtracking(s, i + 2, pointNum);
                pointNum--;
                s.erase(s.begin() + i + 1);
            } else {
                break;
            }
        }
    }

    vector<string> restoreIpAddresses(string s) {
        backtracking(s,0,0);
        return result;
    }
};

96.不同的二叉搜索树

要返回满足题意的二叉搜索树种类数
同时呢,n个节点的二叉搜索树的构建可以由n-1个节点的二叉搜索树推导出来
所以是DP

  1. 确定dp数组(dp table)以及下标的含义
    leetcode每日一题39_第1张图片如图,头节点为1时,其左子树节点数量为0,右子树节点数量为2,节点数为0有1种构建方式,节点数为2有两种构建方式,所以头节点为1时,有1*2种构建方式
    那么我们可以令dp[i]为当节点数为i时,有几种不同的构建方式

  2. 确定递推公式

dp[i]+=dp[j-1]*dp[i-j],j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量

  1. dp数组如何初始化

节点数为0有1种构建方式

dp[0]=1;
  1. 确定遍历顺序
for(int i=1;i<=n;i++)
	for(int j=1;j<=i;j++)
		dp[i]+=dp[j-1]*dp[i-j];
  1. 举例推导dp数组

最后代码如下:

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
                dp[i]+=dp[j-1]*dp[i-j];
        return dp[n];
    }
};

95.不同的二叉搜索树II

这道题要返回所有的二叉搜索树了,那就是回溯了
涉及到树,后序遍历

  1. 确定递归参数
    因为要对返回的左右子树的结果构造二叉平衡树,所以需要返回值,返回值为当前构造的树的根节点的集(因为可以有多个节点都可做当前子树的根节点
    因为要构建左右子树,且是平衡二叉树,所以从[left,right]中选数字i作为根节点,[left,i-1]就是左子树可选节点,[i+1,right]就是右子树可选节点
vector<TreeNode*> backtracking(int left,int right)
  1. 回溯函数终止条件
    当left>right时,就没法产生子树了,也就返回空节点
if(left>right)
{
    ans.push_back(nullptr);
    return ans;
}
  1. 回溯搜索的遍历过程
    在[left,right]中遍历每一个i,作为根节点
    先得到左右子树根节点的候选集
vector<TreeNode*> left_nodes=backtracking(left,i-1);
vector<TreeNode*> right_nodes=backtracking(i+1,right);

i左边的序列可以作为左子树结点,且左儿子可能有多个,所以有vector left_nodes = generate(left, i - 1);;
i右边的序列可以作为右子树结点,同上所以有vector right_nodes = generate(i + 1, right);;

因此,产生的以当前i为根结点的子树有left_nodes.size() * right_nodes.size()个,遍历每种情况,即可生成以i为根节点的子树序列;

for(TreeNode* left_node:left_nodes)
    for(TreeNode* right_node:right_nodes)
    {
        TreeNode* tmp=new TreeNode(i);
        tmp->left=left_node;
        tmp->right=right_node;
        ans.push_back(tmp);
    }

即:

for(int i=left;i<=right;i++){
   vector<TreeNode*> left_nodes=backtracking(left,i-1);
   vector<TreeNode*> right_nodes=backtracking(i+1,right);
   for(TreeNode* left_node:left_nodes)
       for(TreeNode* right_node:right_nodes)
       {
           TreeNode* tmp=new TreeNode(i);
           tmp->left=left_node;
           tmp->right=right_node;
           ans.push_back(tmp);
       }
}

整理得到整个代码:

class Solution {
public:
    vector<TreeNode*> backtracking(int left,int right){
        vector<TreeNode *> ans;
        if(left>right)
        {
            ans.push_back(nullptr);
            return ans;
        }
        for(int i=left;i<=right;i++){
            vector<TreeNode*> left_nodes=backtracking(left,i-1);
            vector<TreeNode*> right_nodes=backtracking(i+1,right);
            for(TreeNode* left_node:left_nodes)
                for(TreeNode* right_node:right_nodes)
                {
                    TreeNode* tmp=new TreeNode(i);
                    tmp->left=left_node;
                    tmp->right=right_node;
                    ans.push_back(tmp);
                }
        }
        return ans;
    }
    vector<TreeNode*> generateTrees(int n) {
        return backtracking(1,n);
    }
};

你可能感兴趣的:(leetcode,算法,职场和发展)