Leetcode 91-100刷题笔记(非困难题目)

91.解码方法

该题目有点类似于与跳台阶问题,使用的是动态规划算法。

我们可以选择截取后面一位,或者截取后面两位,如果后面一位符合逻辑则加上前面的dp值,两位也是一样的。

1.一位需要保证值 > 0
2.两位需要保证 10 <= 数值 <= 26
满足以上条件才能说明是合格的编码,才能够截取。

    //解码(动态规划)
    public int numDecodings(String s) {
        int len = s.length();
        
        int[] dp =  new int[len + 1];
        dp[0] = 1;

        for(int i=1; i<dp.length; i++) {
            //截取一位
            int one = Integer.parseInt(s.substring(i-1,i));
            if(one > 0) {
                //把最后一位分开
                dp[i] += dp[i - 1];
            }
            //截取两位(需要长度 > 1,才能够截取两位)
            if(i > 1) {
                int two = Integer.parseInt(s.substring(i-2,i));
                //保证他是一个合法的两位数
                if(two <= 26 && two >= 10) {
                    dp[i] += dp[i - 2];
                }
            }
            
        }
        return dp[len];
    }

92.反转链表(部分反转)

这个题目算是链表的最终难度题目,依靠的就是指针的移动,和来回指向问题,这个题目需要手写一次最好。

    //部分反转链表
    public ListNode reverseBetween(ListNode head, int m, int n) {
        //获取需要转换的次数
        int changeTimes = n - m;
        ListNode prev = new ListNode();
        ListNode tail = head;
        prev.next = head;

        //找到反转开始结点
        for(int i=1; i<m; i++) {
            prev = prev.next;
            tail = tail.next;
        }

        for(int i=0; i<changeTimes; i++) {
            //获得下一个结点
            ListNode next = tail.next;
            //获得下两个结点(这里不需要判断Null,因为next最多就到尾结点停止)
            ListNode next2 = next.next;

            // 3 -> head(反转后的头结点)
            next.next = prev.next;
            // tail -> 4
            tail.next = next2;
            // prev -> 3(新头结点)
            prev.next = next;
        }
        return m == 1 ? prev.next : head;
    }

93.复原IP地址

首先我们需要了解IP地址的格式(这里指IPV4),总共有四部分,每部分八位(0-255),但是需要注意如果以0开头则只能为0,不能为01,011。

下面部分的话就是简单的回溯算法。

    //复原IP地址
    public List<String> restoreIpAddresses(String s) {
        List<String> list = new ArrayList<>();
        //说明格式存在问题
        if(s.length() < 4 || s.length() > 12) {
            return list;
        }
        dfs(list,new StringBuilder(),s,0,0);
        return list;
    }

    public void dfs(List<String> list,StringBuilder stringBuilder, String s, int index ,int times) {
        if(times == 4) {
            if(index == s.length()) {
                //去除最后多余的一点 .
                list.add(stringBuilder.substring(0,stringBuilder.length() - 1));
            }
            return;
        }
        //最多截取三位(i 为截取的右边界,开区间)
        for(int i=index+1; i<=index+3; i++) {
            if(i > s.length()) {
                break;
            }
            //截取字符串
            String net = s.substring(index,i);
            if(isValid(net)) {
                stringBuilder.append(Integer.parseInt(net));
                stringBuilder.append(".");
                dfs(list,stringBuilder,s,i,times + 1);
                stringBuilder.delete(stringBuilder.length()-1-net.length(),stringBuilder.length());
            }
        }
    }

    //判断是否合法
    public boolean isValid(String net) {
        int len = net.length();

        //以 0 开头
        if(net.startsWith("0")) {
            return len <= 1;
        }
        return Integer.parseInt(net) <= 255;
    }

94.二叉树的中序遍历

基础题

    //中序遍历
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        inorder(list,root);
        return list;
    }
    
    public void inorder(List<Integer> list, TreeNode root) {
        if(root == null) {
            return;
        }
        
        inorder(list,root.left);
        list.add(root.val);
        inorder(list,root.right);
    }

95.不同的二叉搜索树2

这个题目是生成二叉搜索树。我们首先需要知道二叉搜索树的定义:

定义:
1.二叉搜索树的一个结点,其左子树值均小于该节点,其右子树均大于该节点。
2.左右两子树也是二叉搜索树

所以我们可以将其分治思想,我们选择获得其左右子树的所有集合,然后将其对应遍历拼接即可。(我们所需要知道的就是生成所需要的元素,即start,end)

    //生成二叉搜索树
    public List<TreeNode> generateTrees(int n) {
        if(n == 0) {
            return new ArrayList<>();
        }
        return generateTrees(1,1);
    }
    
    public List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> ans = new ArrayList<>();
        if(start > end) {
            ans.add(null);
            return ans;
        }
        
        for(int i=start; i<=end; i++) {
            //生成左右子树
            List<TreeNode> lefts = generateTrees(start,i - 1);
            List<TreeNode> rights = generateTrees(i + 1,end);
            
            //创建根结点
            for(TreeNode left : lefts) {
                for(TreeNode right: rights) {
                    TreeNode root = new TreeNode(i);
                    root.left = left;
                    root.right = right;
                    ans.add(root);
                }
            }
        } 
        return ans;
    }

96.不同的二叉搜索树

该题目就是使用动态规划的思想,具体的思路还是选取一个值作为根结点,然后将其左右结点进行一个划分。

我们这里dp保存的值为当前长度下能够生成的最大数量的二叉搜索树。

我们需要创建 n + 1的dp是因为当,根结点左边,或者右边没有值的时候(对应数就是null结点),这也是属于一种子树情况,所以需要进行考虑。

    //生成二叉搜索树
    public int numTrees(int n) {
        int[] dp = new int[n + 1];

        //初始化
        dp[0] = 1;

        // right 是指当前所遍历到的长度
        for(int right=1; right<dp.length; right++) {
            //这里就是以 left 为根结点进行划分
            for(int left=1; left<=right; left++) {
                dp[right] += dp[left - 1] * dp[right - left];
            }
        }

        return dp[n];
    }

98.验证二叉搜索树

该题目是验证二叉搜索树,所以我们可以从二叉搜索树的定义出发(95题在上面有)。

我们可以从定义 1 出发,我们可以为树的结点设定一个范围最小值lower和最大值upper,结点必须满足在这个范围内(开区间)。

如果我们从根结点出发,判断根结点这个位置是否是二叉搜索树,那么我们就像左子树遍历,为左子树设置一个upper(即根结点val),如果遍历右子树则为右子树分配一个lower。

如果左右子树结点的值满足则说明属于部分二叉搜索树,如果要完全的二叉搜索树则需要继续向下遍历左右子树结点,如果出现不满足情况,则这个整体就不是二叉搜索树。

    //验证二叉搜索树
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root,null,null);
    }

    //为查询设立(lower,upper)
    public boolean isValidBST(TreeNode root, Integer lower, Integer upper) {
        if(root == null) {
            return true;
        }

        if(lower != null && root.val <= lower) {
            return false;
        }

        if(upper != null && root.val >= upper) {
            return false;
        }

        //查找左子树和根结点大小关系
        if(!isValidBST(root.left,lower,root.val)) {
            return false;
        }

        //查询右子树和根结点大小关系
        if(!isValidBST(root.right, root.val, upper)) {
            return false;
        }

        return true;
    }

100.相同的树

分情况讨论

    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null) {
            return true;
        }
        //如果只有一个 null, 那肯定是不同的
        if(p == null || q == null) {
            return false;
        }
        
        //验证自身
        if(p.val != q.val) {
            return false;
        }   
        //验证左子树
        if(!isSameTree(p.left,q.left)) {
            return false;
        }
        
        //验证右子树
        if(!isSameTree(p.right,q.right)) {
            return false;
        }
        return true;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值