Day3

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

思路:动态规划

class Solution {
    public int maxProfit(int[] prices) {
       int n = prices.length;
       if(n<2){
           return 0;
       }
       // 动态规划
       // 因为每天只有持有股票和没有持有股票两种情况,因此使用二维数组保存每天两种情况下的最大利润
       // 若当天持有股票,则dp[i][0]=max(dp[i-1][0],-price[i])
       // 若当天没有持有股票,则dp[i][1]=max(dp[i-1][1],price[i]+dp[i-1][0])
       // 初始化dp数组
       int[][]dp=new int[n][2];
       dp[0][0]=-prices[0];
       dp[0][1]=0;
       int maxProfit=0;
       for(int i=1;i<n;i++){
           dp[i][0]=Math.max(dp[i-1][0],-prices[i]);
           dp[i][1]=Math.max(dp[i-1][1],prices[i]+dp[i-1][0]);
           maxProfit=Math.max(maxProfit,Math.max(dp[i][0],dp[i][1]));
       }
       return maxProfit;
    }
}

213. 打家劫舍II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:

输入:nums = [1,2,3]
输出:3

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

思路:思路都在代码里注释

class Solution {
    public int rob(int[] nums) {
         int n = nums.length;
         if(n==0)return 0;
         if(n==1)return nums[0];

         // 动态规划,用一维数组保存,每种情况要么偷窃,要么不偷窃
         // 又因为是环形,所以第一个和最后一个房屋不能都偷窃,即要么第一个房屋不偷窃,要么第二个房屋不偷窃,最后取二者最大值即可.像这样的打家劫舍问题,可以理解为如果确定了不偷窃,相当于没有这个房屋
         // 当遍历到当前房屋时,如果偷窃,则前一个就确定没有偷窃,于是可以将前一个看做没有,则有dp[i]=dp[i-2]+nums[i];如果不偷窃,则dp[i]=dp[i-1];
         // 为了节省空间复杂度,可以选择依次替换
        return       Math.max(myRob(Arrays.copyOfRange(nums,0,nums.length1)),
                              myRob(Arrays.copyOfRange(nums,1,nums.length)));


    }
    private int myRob(int[] nums){
        int pre=0,cur=0,tmp;
        for(int num:nums){
            tmp=cur;
            cur=Math.max(num+pre,cur);
            pre=tmp;
        }
        return cur;
    }
}

47. 全排列II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

思路:在回溯的过程中和全排列I的问题上多了去重,也就是我们要注意树状图的剪枝操作,把没必要的重复情况剪掉,那么问题是减去同一层的还是减去减去枝丫呢?由于排列中可以使用数值相同的不同元素,因此必须保留枝丫,故只剩下同一层可以减去。道理很简单,如果我们按照单调递增的顺序排列原数组,当遍历到每一层相同的数字时,如果排除了当前数字后,剩下的可选项是一样的,因此可以减去这种重复的情况。然后剪枝后,情况和全排列I就没有区别了

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
            Arrays.sort(nums);
            List<Integer>path = new ArrayList<>();
            List<List<Integer>>res = new ArrayList<>();
            boolean[]used=new boolean[nums.length];
            traceBacking(nums,used,path,res);
            return res;



    }
    private void traceBacking(int[]nums,boolean[]used,List<Integer>path,List<List<Integer>>res){
        if(path.size()==nums.length){
            res.add(new ArrayList<Integer>(path));
            return ;
        }
        for(int i =0;i<nums.length;i++){
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false){
                continue;
            }
            if(used[i]==true){
                continue;
            }
            path.add(nums[i]);
            used[i]=true;
            traceBacking(nums,used,path,res);
            used[i]=false;
            path.remove(path.size()-1);
        }



    }

}
最后修改:2024 年 06 月 08 日
如果觉得我的文章对你有用,请随意赞赏