Loading

🔥 LeetCode 热题 HOT 100(41-50)

102. 二叉树的层序遍历

思路:使用队列。

/**
 * 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 List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new LinkedList<>();
        if (root == null) {
            return res;
        }

        Deque<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            // 当前层的结点数目
            int size = queue.size();
            List<Integer> rowList = new LinkedList<>();
			
            // 每个结点出队时,只要孩子结点不为空就加入队列
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                rowList.add(node.val);

                if (node.left != null) {
                    queue.offer(node.left);
                }

                if (node.right != null) {
                    queue.offer(node.right);
                }
            }

            res.add(rowList);
        }

        return res;
    }
}

104. 二叉树的最大深度

思路:递归,根据定义编写代码。

/**
 * 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 maxDepth(TreeNode root) {
        //base case
        if (root == null) {
            return 0;
        }

        return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
    }
}

105. 从前序与中序遍历序列构造二叉树

/**
 * 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 TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }

    private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
        //base case
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }

        int rootVal = preorder[preStart];
        // 该步骤可以通过map优化
        int index = findIndexInoreder(rootVal, inorder);
        
        int leftCnt = index - inStart;
        TreeNode root = new TreeNode(rootVal);

        root.left =  build(preorder, preStart + 1, preStart + leftCnt, inorder, inStart, index - 1);
        root.right = build(preorder, preStart + leftCnt + 1, preEnd, inorder, index + 1, inEnd);

        return root;
    }

    private int findIndexInoreder(int rootVal, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) {
            if (rootVal == inorder[i]) {
                return i;
            }
        }

        throw new IllegalArgumentException("can not find :" + rootVal);
    }
}

通过Map优化:

class Solution {
    // 通过 map 存储中序序列中各个元素在数组中的下标
    private Map<Integer, Integer> map = new HashMap<>();

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }

        return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }

    private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
        //base case
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }

        int rootVal = preorder[preStart];
        int index = map.get(rootVal);
        
        int leftCnt = index - inStart;
        TreeNode root = new TreeNode(rootVal);

        root.left =  build(preorder, preStart + 1, preStart + leftCnt, inorder, inStart, index - 1);
        root.right = build(preorder, preStart + leftCnt + 1, preEnd, inorder, index + 1, inEnd);

        return root;
    }
}

114. 二叉树展开为链表

思路:递归:向将左右孩子分别展平,然后先将展平后的左子树连接右孩子上,再将展平后的右子树连接上。

/**
 * 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 void flatten(TreeNode root) {
        flat(root);
    }

    private TreeNode flat(TreeNode root) {
        //base case
        if (root == null) {
            return null;
        }

        // 先分别将左右子树展平并返回头结点
        TreeNode left = flat(root.left);
        TreeNode right = flat(root.right);

        // 将左子树置为空
        root.left = null;

        // 先将展平后的左子树连接在右孩子上
        root.right = left;

        // 找到右子树的最右一个结点
        TreeNode temp = root;
        while (temp.right != null) {
            temp = temp.right;
        }

        // 连接展平后的右子树
        temp.right = right;

        return root;
    }
}

121. 买卖股票的最佳时机

思路:动态规划。

股票买卖问题通用解法:

状态:dp[i][k][0],在第i天结束时,允许交易k次的情况下,最终不持有股票能获得的最大利润;dp[i][k][1],在第i天结束时,允许交易k次的情况下,最终持有股票能获得的最大利润。

base case:dp[-1][k][0] = 0、dp[-1][k][1] = -Infinitydp[i][0][0] = 0、dp[i][0][1] = -Infinity。其中dp[-1][k][1] = dp[i][0][1] = -Infinity 表示在没有进行股票交易时不允许持有股票。备注:Java中可以用Integer.MIN_VALUE来表示-Infinity

转移方程:dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]) dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])。前者表示:第i天不持有股票可能是前一天就不持有,或者前一天持有但今天卖出去了。后者表示:第i天持有股票可能是前一天就持有,或者前一天不持有但今天买入了。(这里选择买入时消耗一次交易机会

最终最大利润是dp[len - 1][k][0],因为结束时持有 0 份股票的收益一定大于持有 1 份股票的收益。其中len表示prices数组长度。

根据通用解法有:

dp[i][k][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])

dp[i][k][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i]) = max(dp[i - 1][1][1], -prices[i]) ,其中 dp[i][0][0] = 0。其中 k始终为1,可以简化:

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;

        // dp[i][0] 表示第i天,不持有股票获得的最大收益(允许交易一次)
        // dp[i][1] 表示第i天,持有股票获得的最大收益
        int[][] dp = new int[len][2];

        // base case
        // 第0天不持有股票所能获得的最大利润
        dp[0][0] = 0;
        // 第0天持有股票所能获得的最大利润
        dp[0][1] = -prices[0];

        // 状态转移
        for (int i = 1; i < len; 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],  -prices[i]);
        }

        // 肯定是不持有股票时利润最大
        return dp[len - 1][0];
    }
}

由于第 i 天的最大收益只和第 i - 1 天的最大收益相关,空间复杂度可以由O(n) 降到 O(1)。空间优化:

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;

        //base case
        int dp0 = 0;
        int dp1 = -prices[0];

        //状态转移
        for (int i = 1; i < len; i++) {
            dp0 = Math.max(dp0, dp1 + prices[i]);
            dp1 = Math.max(dp1,  -prices[i]);
        }

        return dp0;
    }
}

推荐题解:股票问题系列通解(转载翻译)

124. 二叉树中的最大路径和

思路:递归

/**
 * 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 maxPathSum(TreeNode root) {
        maxSumToDescendant(root);
        return maxPathSum;
    }
    
	//先初始化为最小值
    private int maxPathSum = Integer.MIN_VALUE;
    
    // 结点到 子孙(不一定包含叶子结点) 结点的最大路径和(最少为其自身一个结点)
    private int maxSumToDescendant(TreeNode root) {
        if (root == null) {
            return 0;
        }
		
        // 小于0则认为对最大路径和没有贡献
        int left = Math.max(0, maxSumToDescendant(root.left));
        int right = Math.max(0, maxSumToDescendant(root.right));
        
        maxPathSum = Math.max(maxPathSum, left + root.val + right);

        return root.val + Math.max(left, right);
    }
}

128. 最长连续序列

class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }

        int maxLen = 0;
        for (int num : nums) {
            // 找到包含 num 的连续序列的下边界
            if (set.contains(num - 1)) {
                continue;
            }

            // 找到下边界后开始计算长度
            int len = 1;
            while (set.contains(num + 1)) {
                len++;
                num++;
            }

            maxLen = Math.max(maxLen, len);
        }

        return maxLen;
    }
}

136. 只出现一次的数字

思路:位运算,每个元素依次异或。

i ^ 0 = i;

i ^ i = 0;

且异或满足交换律和结合律。

class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;

        for (int num : nums) {
            res = res ^ num;
        }

        return res;
    }
}

139. 单词拆分

思路:动态规划

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet<>();
        for (String str : wordDict) {
            set.add(str);
        }

        int len = s.length();

        // 状态: s 中前 i 个字符能否拆分成功
        boolean[] dp = new boolean[len + 1];

        // base case
        dp[0] = true;

        // 状态转移
        // s[0, i]能否被分割取决于:区间[j, i]是否属于set和dp[j]的值(前j个字符 [0, j - 1] 能否被分割),j <= i
        for (int i = 1; i < len + 1; i++) {
            for (int j = 0; j < i; j++) {
                if (set.contains(s.substring(j, i)) && dp[j]) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[len];
    }
}

141. 环形链表

思路:快慢指针,若慢指针最终追上快指针说明有环

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        //没有或只有一个结点说明没有环
        if (head == null || head.next == null) {
            return false;
        }

        ListNode slow = head;
        ListNode fast = head.next;

        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;

            if (slow == fast) {
                return true;
            }
        }

        return false;
        
    }
}
posted @ 2021-07-05 11:46  WINLSR  阅读(53)  评论(0编辑  收藏  举报