代码随想录算法训练营day50|123.买卖股票的最佳时机III188.买卖股票的最佳时机IV 剑指offer21、57、58-I、12、面试题13

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

题目链接

思路:本题中第i天有五种状态:不操作、第一次持有、第一次不持有、第二次持有、第二次不持有。

注意可以同一天进行买卖,而且是多次买卖。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        vector<vector<int>> dp(len,vector<int>(5));
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        dp[0][2]=0;
        dp[0][3]=-prices[0];
        dp[0][4]=0;
        for(int i=1;i<len;i++){
            dp[i][0]=dp[i-1][0];
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
            dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]);
            dp[i][3]=max(dp[i-1][3],dp[i-1][2]-prices[i]);
            dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[len-1][4];
    }
};

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

题目链接

本题和上一题不同的就是,第i天可以有2*k+1种状态,需要额外用一个变量j记录第i天第j次买卖。

至于边界条件,可以代入k=2,进行验证。

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int len=prices.size();
        if(len==0) return 0;
        vector<vector<int>> dp(len,vector<int>(2*k+1,0));
        for(int i=1;i<2*k;i+=2){
            dp[0][i]=-prices[0];//第0天持有一定是买入//错因:是prices[0],不是prices[i]
        }
        for(int i=1;i<len;i++){
            for(int j=0;j<2*k;j+=2){
                dp[i][j+1]=max(dp[i-1][j+1],dp[i-1][j]-prices[i]);
                dp[i][j+2]=max(dp[i-1][j+2],dp[i-1][j+1]+prices[i]);
            }
        }
        return dp[len-1][2*k];       
    }
};

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

题目链接

思路:快排的思路。只要left和right不指向同一个元素,如果left指向的元素一直是奇数,就一直往后移动left指针,如果right指向的元素一直是偶数,就一直往前移动right指针,然后两个指针停止的位置肯定是left指向偶数,right指向奇数,那么将这两个元素进行交换即可,然后再开始下一轮。

class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int left=0,right=nums.size()-1;
        while(left<right){
            while(left<right&&nums[left]%2==1) left++;
            while(left<right&&nums[right]%2==0) right--;
            int tem=nums[left];
            nums[left]=nums[right];
            nums[right]=tem;
        }
        return nums;
    }
};

剑指 Offer 57. 和为s的两个数字

题目链接

思路:一说是在排序数组中查找元素,自然就是二分查找。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i=0;i<nums.size()-1;i++){
            int left=i+1;
            int right=nums.size()-1;
            int fin=target-nums[i];
            while(left<=right){
                int mid=(left+right)/2;
                if(nums[mid]==fin) return vector<int>{nums[i],fin};
                else if(nums[mid]<fin) left=mid+1;
                else{
                    right=mid-1;
                }
            }
        }
        return vector<int>{};
    }
};

剑指 Offer 58 - I. 翻转单词顺序

题目链接

思路:双指针法,快指针用于获取我们想要的元素,满指针指向我们获取的新元素更新在哪里。先去除字符串中多余的空格,然后整体反转+局部反转。其实相当于同时操作新数组和旧数组,外层循环是快指针用于遍历旧数组,然后用满指针更新新数组。

class Solution {
public:
    void reverse(string&s,int start,int end){
        for(int i=start,j=end;i<j;i++,j--){
            swap(s[i],s[j]);
        }
    }
    void removeextraspace(string&s){
        int slow=0;
        for(int fast=0;fast<s.size();fast++){
            if(s[fast]!=' '){//找到新的单词了,然后同时移动快慢指针,但是单词之间要留空格
                if(slow!=0) s[slow++]=' ';
            }
            while(fast<s.size()&&s[fast]!=' '){
                s[slow++]=s[fast++];
            }
        }
        s.resize(slow);//第一处错误,忘记改了
    }
    string reverseWords(string s) {
        if(s.size()==0) return s;
        removeextraspace(s);
        reverse(s,0,s.size()-1);
        int start=0;//用来记录每次找到的单词的起始位置,并且去除空格之后第一个单词的首字母下标一定是0
        for(int i=0;i<=s.size();i++){//第二处错误,边界条件应该有等于,因为这个下标i是为了记录每个单词的末尾后面一个位置
            if(i==s.size()||s[i]==' '){
                reverse(s,start,i-1);
                start=i+1;
            }
        }
        return s;
    }
};

错因:1、更新完新数组之后忘记改字符串的大小了,因为我们是在原数组上直接进行更新的,所以要改变新数组的大小。

2、在反转单词的时候for循环的边界条件写错了,应该有等于,因为此时for循环里的下标i是为了我们记录单词的末尾位置,但是小标i指向的是单词的末尾位置的后一个位置,所以要有等于s.size()。

剑指 Offer 12. 矩阵中的路径

题目链接

思路:回溯法。注意同一个格子的元素不能重复访问,所以将它改成一个#(因为boardword仅由大小写英文字母组成,所以#一定不会被访问),然后回溯的时候再改回去即可。当然也可以用visited数组记录访问情况,但是这样的空间复杂度比较高。

class Solution {
public:
    bool dfs(vector<vector<char>>& board, string word,int k,int i,int j){
        int m=board.size();
        int n=board[0].size();
        if(!(i>=0&&i<m)||!(j>=0&&j<n)||board[i][j]!=word[k]) return false;
        if(k==word.size()-1) return true;
        char temp=board[i][j];
        board[i][j]='#';//防止重复访问
        bool res=dfs(board,word,k+1,i+1,j)||dfs(board,word,k+1,i-1,j)||dfs(board,word,k+1,i,j+1)||dfs(board,word,k+1,i,j-1);
        board[i][j]=temp;
        return res;
    }
    bool exist(vector<vector<char>>& board, string word) {
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                if(dfs(board,word,0,i,j)) return true;
            }
        }
        return false;
    }
};

错因:dfs的递归终止条件中的边界条件写错,写成了i<=m和j<=n,应该是<。

本题因为单词内字符是有顺序的,所以可能前面的字符不符合,但是后面的字符符合了,所以必须要进行回溯。

面试题13. 机器人的运动范围

题目链接

本题和上一题不一样的是,如果遍历到一个格子是不符合条件的,那它一定就是不符合了,所以不用进行回溯。

class Solution {
public:
    int getval(int i,int j){//直接求出两个数的数位之和
        int res=0;
        while(i){
            res+=i%10;
            i/=10;
        }
        while(j){
            res+=j%10;
            j/=10;
        }
        return res;
    }
    int dfs(int i, int j, int k,int m,int n,vector<vector<bool>>& visited){
        if(i>=m||j>=n||getval(i,j)>k||visited[i][j]==true) return 0;//visited[i][j]==true说明该点已经被之前某条路径访问过了,已经计算过了,如果再沿着这个位置往右往下走的话,必然会走重复的路径//就相当于设了个障碍,不能再继续往后搜索了,该点也不能计算,所以返回0
        visited[i][j]=true;
        return 1+dfs(i+1,j,k,m,n,visited)+dfs(i,j+1,k,m,n,visited);
    }
    int movingCount(int m, int n, int k) {
        vector<vector<bool>> visited(m,vector<bool>(n,false));
        return dfs(0,0,k,m,n,visited);
    }
};

注意数位求和的过程。

开始很纠结为什么dfs函数里,visited[i][j]==true,要返回0。因为visited[i][j]==true说明该点已经被之前某条路径经过了,也就是已经计算过了,如果是true就返回0,也就是当前路径经过该点的后面的路径不要再进行搜索了,而且该点也不能进行计算,所以直接返回0。

本题用unordered_set的写法还是不太会,有待继续研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值