2021-06-05

1. 203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例1:

在这里插入图片描述

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]

示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
(1)解法一:
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while(head!=null&&head.val==val){
            head=head.next;
        }
        if(head!=null){
            ListNode pre=head;
            ListNode q=pre.next;
            while(q!=null){
                while(q!=null&&q.val==val){
                    q=q.next;
                }
                pre.next=q;
                pre=q;
                if(q!=null)
                    q=q.next;
            }
        }
        return head;
    }
}

在这里插入图片描述

(2)解法二:加头结点
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //加头结点
        ListNode root=new ListNode(-1,head);
        ListNode pre=root;
        ListNode p=pre.next;
        while(p!=null){
            while(p!=null&&p.val==val){
                p=p.next;
            }
            pre.next=p;
            pre=p;
            if(p!=null)
                p=p.next;
        }
        return root.next;
    }
}

在这里插入图片描述

2. 63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

在这里插入图片描述

网格中的障碍物和空位置分别用 1 和 0 来表示。

在这里插入图片描述

示例一:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

在这里插入图片描述

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j] 为 0 或 1
class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m=obstacleGrid.length;
        int n=obstacleGrid[0].length;
        int[][] dp=new int[m][n];
        boolean flagrow=false,flagcol=false;
        for(int i=0;i<m;i++){
            if(!flagrow&&obstacleGrid[i][0]==0){
                dp[i][0]=1;
            }else{
                flagrow=true;
            }
        }
        for(int i=0;i<n;i++){
            if(!flagcol&&obstacleGrid[0][i]==0){
                dp[0][i]=1;
            }else{
                flagcol=true;
            }
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(obstacleGrid[i][j]==1){
                    dp[i][j]=0;
                }
                else{
                    dp[i][j]=dp[i][j-1]+dp[i-1][j];
                }
            }
        }
        return dp[m-1][n-1];
    }
}

在这里插入图片描述

3. 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 = [0]
输出:0

提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
class Solution {
    public int rob(int[] nums) {
        //改进的点在于最后一间房屋跟第一间房屋也是有关系的。似乎只是最后一个有区别
        //我怎么确定第一件房屋是否被盗窃了呢?
        //分成不要第一个跟要第一个来做试一试。
        //不要第一个,那么从2-n跟之前一样,要第一个,那么最后一个要不了,从1-n-1跟之前一样。
        int[] dp=new int[nums.length+1];
        int a0,a1,a2;
        int b0,b1,b2;
        a0=a1=0;
        b0=0;
        b1=nums[0];
        int max1,max2;
        for(int i=2;i<=nums.length;i++){
            a2=Math.max(a1,a0+nums[i-1]);
            a0=a1;
            a1=a2;
            if(i<nums.length){
                b2=Math.max(b0+nums[i-1],b1);
                b0=b1;
                b1=b2;
            }
        }
        return Math.max(a1,b1);
    }
}

在这里插入图片描述

4. 337. 打家劫舍 III

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:
输入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

输出: 7 
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:
输入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
(1)递归法,但是超时!!!
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        //感觉递归简单一些
        if(root==null){
            return 0;
        }
        if(root.left!=null&&root.right!=null){
            return Math.max(root.val+rob(root.left.left)+rob(root.left.right)+rob(root.right.left)+rob(root.right.right),rob(root.left)+rob(root.right));
        }
        else if(root.left!=null){
            return Math.max(root.val+rob(root.left.left)+rob(root.left.right),rob(root.left));
        }
        else if(root.right!=null){
            return Math.max(root.val+rob(root.right.left)+rob(root.right.right),rob(root.right));
        }
        else{
            return root.val;
        }
    }
}
(2)解法二在解法一的基础上增加了记忆化搜索的算法。

其实,动态规划特点就是有着重叠的子问题,所以使用递归的话就会重复计算很多次,会很容易就超时,所以可以采用迭代的方法用数组记录,但是这一题明显是二叉树,迭代不行,递归才是王道,那就用记忆的方式,用map保存下已经计算出来的某个以某个结点为根节点的可以偷得最大价值。之后递归的过程中可以使用已经计算出来的。

class Solution {
    Map<TreeNode,Integer> map=new HashMap<>();
    public int rob(TreeNode root) {
        if(root==null){
            return 0;
        }
        if(map.containsKey(root)){
            return map.get(root);
        }
        int value1=root.val;
        int value2=0;
        if(root.right!=null){
            value1+=rob(root.right.left)+rob(root.right.right);
            value2+=rob(root.right);
        }
        if(root.left!=null){
            value1+=rob(root.left.right)+rob(root.left.left);
             value2+=rob(root.left);
        }
        map.putIfAbsent(root,Math.max(value1,value2));
        return Math.max(value1,value2);
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值