Leetcode刷题笔记(6)

主题:动态规划

(1)45. 跳跃游戏 II

我的思路:动态规划;数组记录到当前位置的最少步数,这个点可以到达的点的最少步数等于该点步数+1,如果已经有其他点可以到达该点,则取两者的最小值。

class Solution {
    public int jump(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len];
        dp[0] = 0;
        for(int i = 0; i < len; i++){
            for(int j = 1; j <= nums[i];j++){
                if(i+j < len){
                    if(dp[i+j] > 0){
                        dp[i+j] = (dp[i]+1 < dp[i+j]) ? dp[i]+1 : dp[i+j];
                    }else{
                        dp[i+j] = dp[i] + 1;
                    }
                }
            }
        }
        return dp[len-1];
    }
}

题解思路:贪心算法;

class Solution {
    public int jump(int[] nums) {
        int length = nums.length;
        int end = 0;
        int maxPosition = 0; 
        int steps = 0;
        for (int i = 0; i < length - 1; i++) {
            maxPosition = Math.max(maxPosition, i + nums[i]); 
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }
        return steps;
    }
}

(2)62. 不同路径

我的思路:动态规划;当前点的路径数= 左边点的路径数+上面点的路径数 (如果这些点存在的话)

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(i == 0 && j == 0){
                    dp[i][j] = 1;
                }else if(i == 0 && j > 0){
                    dp[i][j] += dp[i][j-1]; 
                }else if(i > 0 && j == 0){
                    dp[i][j] += dp[i-1][j];
                }else{
                    dp[i][j] += dp[i][j-1] + dp[i-1][j];
                }
            }
        }
        return dp[m-1][n-1];
    }
}

(3)95. 不同的二叉搜索树 II(错1)

题解思路:回溯;

class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) {
            return new LinkedList<TreeNode>();
        }
        return generateTrees(1, n);
    }

    public List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> allTrees = new LinkedList<TreeNode>();
        if (start > end) {
            allTrees.add(null);
            return allTrees;
        }
        // 枚举可行根节点
        for (int i = start; i <= end; i++) {
            // 获得所有可行的左子树集合
            List<TreeNode> leftTrees = generateTrees(start, i - 1);

            // 获得所有可行的右子树集合
            List<TreeNode> rightTrees = generateTrees(i + 1, end);

            // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上
            for (TreeNode left : leftTrees) {
                for (TreeNode right : rightTrees) {
                    TreeNode currTree = new TreeNode(i);
                    currTree.left = left;
                    currTree.right = right;
                    allTrees.add(currTree);
                }
            }
        }
        return allTrees;
    }
}

(4)328. 奇偶链表

我的思路:用另外一个链表存储偶数节点,先获取下一个偶数节点,将它连接至另外一个链表中,将其从原链表删除。最后将两个链表合并

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode dummy = new ListNode(0,head);
        ListNode doubleHead = new ListNode();
        ListNode pre = dummy;
        ListNode cur = dummy.next;
        ListNode node = doubleHead;
        while(cur != null && cur.next != null){
            node.next = cur.next;
            cur.next = cur.next.next;

            node = node.next;
            node.next = null;

            pre = pre.next;
            cur = cur.next;
        }
        if(cur == null){
            pre.next = doubleHead.next;
        }else if(cur.next == null){
            cur.next = doubleHead.next;
        }
        return dummy.next;

    }
}

优化:直接交替next,不用删除

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode evenHead = head.next;
        ListNode odd = head, even = evenHead;
        while (even != null && even.next != null) {
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;
        }
        odd.next = evenHead;
        return head;
    }
}

(5)91. 解码方法

我的思路:动态规划,先确定dp[0] 和dp[1],后面的项与前两位有关,枚举增加一位数字可能的所有情况。

class Solution {
    public int numDecodings(String s) {
        if(s.charAt(0) == '0'){
            return 0;
        }
        int len = s.length();
        if(len == 1){
            return 1;
        }
        int[] dp = new int[len];
        dp[0] = 1;
        int n = s.charAt(0)-'0';
        int m = s.charAt(1)-'0';
        if(m == 0){
            if(n > 2){
                return 0;
            }else{
                dp[1] = 1;
            }
        }else{
            if(n*10 + m > 26){
                dp[1] = 1;
            }else{
                dp[1] = 2;
            }
        }
        for(int i = 2; i < len; i++){
            n = s.charAt(i-1)-'0';
            m = s.charAt(i)-'0';
            if(m == 0){
                if(n <= 2 && n > 0){
                    dp[i] = dp[i-2];
                }else{
                    return 0;
                }
            }else if(n*10 + m > 26 || n == 0){
                dp[i] = dp[i-1];
            }else{
                dp[i] = dp[i-1] + dp[i-2];
            }
        }
        return dp[len-1];
    }
}

题解思路:在开头增加一位,只要当前数字不为零,一定有dp[i-1]种组合,如果可以与前一位数字组合,再加dp[i-2]。

class Solution {
    public int numDecodings(String s) {
        int n = s.length();
        int[] f = new int[n + 1];
        f[0] = 1;
        for (int i = 1; i <= n; ++i) {
            if (s.charAt(i - 1) != '0') {
                f[i] += f[i - 1];
            }
            if (i > 1 && s.charAt(i - 2) != '0' && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
                f[i] += f[i - 2];
            }
        }
        return f[n];
    }
}

再优化:以为只与前两位有关,可以用三个数字进行迭代,而不用数组存储所有位置的分组数,节约空间。

class Solution {
    public int numDecodings(String s) {
        int n = s.length();
        // a = f[i-2], b = f[i-1], c=f[i]
        int a = 0, b = 1, c = 0;
        for (int i = 1; i <= n; ++i) {
            c = 0;
            if (s.charAt(i - 1) != '0') {
                c += b;
            }
            if (i > 1 && s.charAt(i - 2) != '0' && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
                c += a;
            }
            a = b;
            b = c;
        }
        return c;
    }
}

(6)97. 交错字符串

我的思路:单个字母遍历,如果遇到字母同时出现在s1和s2中,递归搜索子串。结果超时了。。。

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int temp1 = 0;
        int temp2 = 0;
        int cur = 0;
        int len1 = s1.length();
        int len2 = s2.length();
        int len3 = s3.length();
        if(len1 + len2 != len3){
            return false;
        }
        for(int i = 0; i < len3; i++){
            if((temp1 < len1 && s3.charAt(i) == s1.charAt(temp1)) && 
            (temp2 < len2 && s3.charAt(i) == s2.charAt(temp2))){
                return isInterleave(s1.substring(temp1+1,len1), s2.substring(temp2,len2), s3.substring(i+1,len3)) || isInterleave(s1.substring(temp1,len1), s2.substring(temp2+1,len2), s3.substring(i+1,len3));

            }else if(temp1 < len1 && s3.charAt(i) == s1.charAt(temp1)){
                temp1++;
            }else if(temp2 < len2 && s3.charAt(i) == s2.charAt(temp2)){
                temp2++;
            }else{
                return false;
            }
        }
        return true;

    }

(7)382. 链表随机节点

思路:蓄水池算法,取前k个节点,从第k个后面的节点开始,第i个节点与k个节点任意一节点交换的概率为k/(k+i)。

class Solution {
    ListNode head;
    public Solution(ListNode head) {
        this.head = head;
    }
    public int getRandom() {
        int res = head.val;
        ListNode cur = head.next;
        int count = 1;
        Random random = new Random();

        while(cur != null){
            if(random.nextInt(count + 1) == 0){
                res = cur.val;
            }
            cur = cur.next;
            count++;
        }
        return res;
    }
}

方法二:先遍历一遍链表,获得链表的长度,随机获取链表长度中的一个数字,该数字为随机选取到的节点的序号。

(8)118. 杨辉三角

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        for(int i = 0; i < numRows; i++){
            res.add(new ArrayList<Integer>());
            for(int j = 0; j < i+1; j++){
                int temp;
                if(j == 0 || j == i){
                    temp = 1;
                }else{
                    temp = res.get(i-1).get(j-1) + res.get(i-1).get(j);
                }
                res.get(i).add(temp);
            }
        }
        return res;

    }
}

(9)120. 三角形最小路径和

我的思路:逐层动态规划,到每个点的最小路径=可以到达它的前两个点中更小的那个的点的最小路径+本身的距离。

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        List<List<Integer>> dp = new ArrayList<List<Integer>>();
        int level = triangle.size();
        for(int i = 0; i < level; i++){
            dp.add(new ArrayList<Integer>());
            for(int j = 0; j < i + 1; j++){
                int cur = triangle.get(i).get(j);
                if(i == 0){
                    dp.get(i).add(cur);
                }else{
                    if(j == 0){
                        dp.get(i).add(cur +  dp.get(i-1).get(j));
                    }else if(j == i){
                        dp.get(i).add(cur +  dp.get(i-1).get(j-1));
                    }else{
                        int temp = dp.get(i-1).get(j) < dp.get(i-1).get(j-1) ? dp.get(i-1).get(j) : dp.get(i-1).get(j-1);
                        dp.get(i).add(temp+cur);
                    }
                }
            }
        }
        List<Integer> last = dp.get(level-1);
        int res = last.get(0);
        for(int i = 1; i < level; i++){
            if(last.get(i) < res){
                res = last.get(i);
            }
        }
        return res;
    }
}

空间优化:这一层的最小路径只有上一层的最小路径确定,可以用一个一维数组存储即可。

(10)122. 买卖股票的最佳时机 II(错1)

题解思路一:动态规划;每次交易结束,手里只有一支股票或者没有股票的状态;dp[i][0]表示第i天交易后手里没有股票的最大利润,dp[i][1]有一支股票的最大利润,列出状态转移方程dp[i][0] = max{dp[i-1][0], dp[i-1][1] + price[i]}; dp[i][1] = max{dp[i-1][1], dp[i-1][0] - price[i]}

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < n; ++i) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}

题解思路二:贪心算法,因为股票买卖次数没有限制,问题等价于找x个不相交的区间,使得区间两头的价格差值之和最大。

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for(int i = 1; i < prices.length; i++){
            if(prices[i] > prices[i-1]){
                res += prices[i] - prices[i-1];
            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值