leetcode 100-150题-java版(按顺序,不分专题)

这篇博客详细介绍了LeetCode 100到150题的Java解题思路,涵盖了二叉树的各种遍历、构建、属性检查等问题,包括对称二叉树、层序遍历、锯齿形层次遍历、最大深度等,还涉及了路径总和、买卖股票最佳时机等经典算法题目。通过阅读,读者可以深入理解二叉树和动态规划等概念。
摘要由CSDN通过智能技术生成

(101)对称二叉树

给定一个二叉树,检查它是否是镜像对称的。
思路:递归,或者用栈与队列模拟递归。当需要进行这种成对儿的出入,进行比较,对称否/镜像否 等递归时,貌似队列更合适。

/**
 * 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;
 *     }
 * }
 */
import java.util.*;

class Solution {
    public boolean isSymmetric(TreeNode root) {
        // 递归
        // if(root==null)
        //     return true;
        // return is(root.left, root.right);

        // 队列迭代
        if(root==null)
            return true;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root.left);
        queue.offer(root.right);
        while(!queue.isEmpty()){
            TreeNode left = queue.poll();
            TreeNode right = queue.poll();
            if(left==null && right==null) continue;
            else if(left==null || right==null) return false;
            if(left.val!=right.val) return false;
            queue.offer(left.left);
            queue.offer(right.right);
            queue.offer(left.right);
            queue.offer(right.left);
        }
        return true;
    }

    public boolean is(TreeNode left, TreeNode right){
        if(left==null && right==null) return true;
        else if(left==null) return false;
        else if(right==null) return false;
        else{
            if(left.val!=right.val) return false;
            else{
                return is(left.left, right.right) && is(left.right, right.left);
            }
        }
    }
}

(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;
 *     }
 * }
 */
import java.util.*;

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root==null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            List<Integer> tmp = new ArrayList<>();
            for(int i=0; i<size; i++){
                TreeNode p = queue.poll();
                tmp.add(p.val);
                if(p.left!=null) queue.offer(p.left);
                if(p.right!=null) queue.offer(p.right);

            }
            res.add(tmp);
        }
        return res;
    }
}

(103)二叉树的锯齿形层次遍历

给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

思路:剑指offer的总结上有。

/**
 * 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>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        if(root==null){
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        ArrayList<Integer> temp = new ArrayList<>();
        boolean flag = true;
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            for(int i=0; i< size; i++){
                TreeNode p = queue.poll();
                if(flag){
                    temp.add(p.val);
                } else{
                    temp.add(0, p.val);
                }
                if(p.left!=null) queue.offer(p.left);
                if(p.right!=null) queue.offer(p.right);
            }
            list.add(new ArrayList<>(temp));
            temp.clear();
            flag = !flag;
        }
        return list;
    }
}

(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 {
    int maxdepth=0;;
    public int maxDepth(TreeNode root) {
        preOrder(root, 0);
        return maxdepth;
    }

    public void preOrder(TreeNode root, int depth){
        if(root==null){
            return ;
        }
        maxdepth = Math.max(maxdepth, depth+1);
        preOrder(root.left, depth+1);
        preOrder(root.right, depth+1);
    }
}

(105)从前序和中序遍历构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

思路:剑指offer的总结上有。

/**
 * 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[] pre, int[] in) {
        if(pre == null || in == null || pre.length == 0 || in.length == 0 || pre.length != in.length) {
            return null;
        }
        return getRootNode(pre, 0, pre.length - 1, in , 0, in.length - 1);
        
    }
    
    private TreeNode getRootNode(int [] pre, int preStart, int preEnd, int [] in, int inStart, int inEnd){
        if(preStart > preEnd || inStart > inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(pre[preStart]);
        for(int i = inStart; i <= inEnd; i++) {
            if(pre[preStart] == in[i]) {
                root.left = getRootNode(pre, preStart+1, preStart+i-inStart, in, inStart, i-1);
                root.right = getRootNode(pre, preStart+i-inStart+1, preEnd, in, i+1, inEnd);
            }
        }
        return root;
    }
}

(106)从中序和后序遍历构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

思路:剑指offer的总结上有。

/**
 * 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[] in, int[] post) {
        if(post == null || in == null || post.length == 0 || in.length == 0 || post.length != in.length) {
            return null;
        }
        return getRootNode(post, 0, post.length - 1, in , 0, in.length - 1);
        
    }
    
    private TreeNode getRootNode(int [] post, int postStart, int postEnd, int [] in, int inStart, int inEnd){
        if(postStart > postEnd || inStart > inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(post[postEnd]);
        for(int i = inStart; i <= inEnd; i++) {
            if(post[postEnd] == in[i]) {
                root.left = getRootNode(post, postStart, postStart+i-inStart-1, in, inStart, i-1);
                root.right = getRootNode(post, postStart+i-inStart, postEnd-1, in, i+1, inEnd);
            }
        }
        return root;
    }
}

(107)二叉树的层序遍历-2

给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

/**
 * 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>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root==null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            List<Integer> tmp = new ArrayList<>();
            for(int i=0; i<size; i++){
                TreeNode p = queue.poll();
                tmp.add(p.val);
                if(p.left!=null) queue.offer(p.left);
                if(p.right!=null) queue.offer(p.right);

            }
            res.add(0, tmp);
        }
        return res;
    }
}

(108)将有序数组转化为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 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 TreeNode sortedArrayToBST(int[] nums) {
        int n = nums.length;
        return tobst(nums, 0, n-1);
    }

    public TreeNode tobst(int [] num, int left, int right){
        if(left>right) return null;
        int mid = (left+right)/2;
        TreeNode root = new TreeNode(num[mid]);
        root.left = tobst(num, left, mid-1);
        root.right = tobst(num, mid+1, right);
        return root;
    }
}

(109)有序链表转化为二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
/**
 * 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;
 *     }
 * }
 */
import java.util.ArrayList;

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        ArrayList<Integer> data =  new ArrayList<>();
        ListNode p = head;
        while(p!=null){
            data.add(p.val);
            p=p.next;
        }

        int n = data.size();
        int [] num = new int[n];
        for(int i=0; i<n; i++){
            num[i] = data.get(i);
        }
        return tobst(num, 0, n-1);
    }

    public TreeNode tobst(int [] num, int left, int right){
        if(left>right) return null;
        int mid = (left+right)/2;
        TreeNode root = new TreeNode(num[mid]);
        root.left = tobst(num, left, mid-1);
        root.right = tobst(num, mid+1, right);
        return root;
    }
}

(110)平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

思路:树的问题果然是逃不过递归,真香。

/**
 * 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 {
    boolean isbalanced = true;
    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        int h = 0;
        h = height(root);
        return isbalanced;
    }

    public int height(TreeNode root){
        if(root==null) return 0; // 为null,高度则为0
        int left_height = height(root.left); 
        int right_height = height(root.right); 
        if(Math.abs(left_height-right_height)>1)
            isbalanced = false;
        return 1+Math.max(left_height, right_height);
    }
}

(112)路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。

/**
 * 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 {
    boolean flag = false;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        preOrder(root, targetSum);
        return flag;
    }

    public void preOrder(TreeNode root, int target){
        if(flag==true || root==null){
            return ;
        }
        target-=root.val;
        if(root.left==null && root.right==null){
            if(target==0)
                flag = true;
        }
        preOrder(root.left, target);
        preOrder(root.right, target);
    }
}

(113)路径总和-2

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

/**
 * 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 {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> tmp = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        preOrder(root, targetSum);
        return res;
    }

    public void preOrder(TreeNode root, int target){
        if(root==null){
            return ;
        }
        tmp.add(root.val);
        target-=root.val;
        if(root.left==null && root.right==null){
            if(target==0)
                res.add(new ArrayList<>(tmp));
        }
        preOrder(root.left, target);
        preOrder(root.right, target);
        tmp.remove(tmp.size()-1);
    }
}

(116)填充每个节点的下一个右侧结点指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/
import java.util.*;

class Solution {
    public Node connect(Node root) {
        if(root==null) return null;
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            Node node = queue.poll();
            if(node.left!=null) queue.offer(node.left);
            if(node.right!=null) queue.offer(node.right);
            for(int i=1; i<size; i++){
                Node tmp = queue.poll();
                if(tmp.left!=null) queue.offer(tmp.left);
                if(tmp.right!=null) queue.offer(tmp.right);
                node.next = tmp;
                node = tmp;
            }
            node.next = null;
        }
        return root;
    }
}

(118)杨辉三角

给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

在杨辉三角中,每个数是它左上方和右上方的数的和。

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

import java.util.*;

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> res = new ArrayList<>();
        if(numRows==0) return res;
        if(numRows==1 || numRows==2){
            for(int i=0; i<numRows; i++){
                res.add(new ArrayList<Integer>());
                for(int j=0; j<=i; j++){
                    res.get(i).add(1);
                }
            }
            return res;
        }
        
        // >=3的情况
        res.add(new ArrayList<Integer>());
        res.add(new ArrayList<Integer>());
        res.get(0).add(1);
        res.get(1).add(1);
        res.get(1).add(1);
        for(int i=2; i<numRows; i++){
            res.add(new ArrayList<Integer>());
            res.get(i).add(1);
            for(int j=1; j<i; j++){
                int tmp = res.get(i-1).get(j) + res.get(i-1).get(j-1);
                res.get(i).add(tmp);
            }
            res.get(i).add(1);
        }
        return res;
    }
}

(119)杨辉三角-2

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

示例:
输入: 3
输出: [1,3,3,1]

进阶:
你可以优化你的算法到 O(k) 空间复杂度吗?

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> res = new ArrayList<>();
        if(rowIndex==0){
            res.add(1);
            return res;
        }

        res.add(1);
        for(int i=1; i<=rowIndex; i++){
            res.add(1); // 先保证数的个数符合这行的要求,然后在修改每个位置的值(记住倒序修改值)
            for(int j=i-2; j>=0; j--){
                int tmp = res.get(j+1) + res.get(j);
                res.remove(j+1);
                res.add(j+1, tmp);
            }
        }
        return res;
    }
}

(120)三角形中最小路径和

给定一个三角形 triangle ,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

示例 2:
输入:triangle = [[-10]]
输出:-10

提示:
1 <= triangle.length <= 200;
triangle[0].length == 1;
triangle[i].length == triangle[i - 1].length + 1;
-10^4 <= triangle[i][j] <= 10^4;

进阶:
你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗?

思路:下面代码是利用了二维数组dp。进阶的话可以参考(119)题中的方法,设置一个长为 n 的数组,然后倒序修改数组中的值。

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        int[][] dp = new int [n][n];
        for(int i=0; i<n; i++){
            if(i==0){
                dp[i][0] = triangle.get(0).get(0); 
            } else {
                dp[i][0] = dp[i-1][0] + triangle.get(i).get(0);
            }
        }
        if(n==1)
            return dp[0][0];
        
        for(int i=1; i<n; i++){
            for(int j=1; j<=i; j++){
                if(i==j){
                    dp[i][j] = dp[i-1][j-1] + triangle.get(i).get(j);
                } else {
                    dp[i][j] = triangle.get(i).get(j) + Math.min(dp[i-1][j-1], dp[i-1][j]);
                }
            }
        }
        int ans = Integer.MAX_VALUE;
        for(int i=0; i<n; i++){
            ans = Math.min(ans, dp[n-1][i]);
        }
        return ans;
    }
}

(121)买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

思路:股票买卖问题都去看一眼,https://github.com/labuladong/fucking-algorithm。

class Solution {
    public int maxProfit(int[] prices) {
        // int n = prices.length;
        // if(n==0) return 0;
        // int [][] dp = new int [n][2];
        // for(int i=0; i<n; i++){
        //     if(i==0){
        //         dp[0][0] = 0;
        //         dp[0][1] = -prices[0];
        //         continue;
        //     }
        //     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[n-1][0];

        int n = prices.length;
        if(n==0) return 0;
        int dp_i_0 = 0;
        int dp_i_1 = -prices[0];
        for(int i=1; i<n; i++){
            dp_i_0 = Math.max(dp_i_0, dp_i_1+prices[i]);
            dp_i_1 = Math.max(dp_i_1, -prices[i]);
        }
        return dp_i_0;
    }
}

(122)买卖股票的最佳时机-2

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路:同上

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

(123)买卖股票的最佳时机-3

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路:同上

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

(124)二叉树中最大路径和

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root ,返回其 最大路径和 。

示例:
在这里插入图片描述
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:
树中节点数目范围是 [1, 3 * 10^4];
-1000 <= Node.val <= 1000;

思路:递归牛逼,思路也牛逼。求的是root节点的左右两边两个单边最大值,若单边最大值小于0则最大值设为0;dfs时返回的是途径root的单边最大值;递归时维护一个全局的最大值,也就是想要的结果。

/**
 * 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 {
    // 树的问题就是递归
    int max = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        if(root==null)
            return 0;
        int tmp = dfs(root);
        return max;
    }


    // 后续遍历树,返回经过root的单边最大路径和;同时维护整棵树的最大路径和,即max变量
    public int dfs(TreeNode root){
        // 终止条件
        if(root==null)
            return 0;
        // 计算左边分支最大和,左边分支最大和如果为负数还不如不选择
        int leftMax = Math.max(0, dfs(root.left));
        // 计算右边分支最大和,右边分支最大和如果为负数还不如不选择
        int rightMax = Math.max(0, dfs(root.right));
        // 路径最大的一种可能是 left->root->right,而不向root的父节点延申。为什么不延申,以示例2为例,root为20时,
        // 向父节点延申会导致20节点重复利用,所以向20父节点延申的话就只能返回20节点的一条单边。而本次dfs调用的root为20,
        // 是由父节点-10调用的,所以递归结束return时也只能返回一个经过20的单边
        max = Math.max(max, root.val+leftMax+rightMax);
        // 向root的父节点返回经过root的单边分支的最大路径和
        return root.val + Math.max(leftMax, rightMax);
    }
}

(125)验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true

示例 2:
输入: “race a car”
输出: false

class Solution {
    public boolean isPalindrome(String s) {
        int n = s.length();
        if(n==0)
            return true;
        int left = 0;
        int right = n-1;
        while(left<right){
            while(left<right && !Character.isLetterOrDigit(s.charAt(left))){
                left++;
            }
            while(left<right && !Character.isLetterOrDigit(s.charAt(right))){
                right--;
            }
            if(left<right){
                if(Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))){
                    return false;
                }
                left++;
                right--;
            }
        }
        return true;
    }
}

注意:注意三个函数即可。Character.isLetterOrDigit() 可以判断一个char是不是字母或数字的字符;Character.toLowerCase()会把字母字符转为小写,数字字符不变;Character.toUpperCase()会把字母字符转为大写,数字字符不变。

(128)最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

进阶:你可以设计并实现时间复杂度为 O(n) 的解决方案吗?

示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

提示:
0 <= nums.length <= 10^4;
-10^9 <= nums[i] <= 10^9;

思路:
首先找到nums中有哪些元素可以当作连续序列的左边界。
假设a为一个连续序列的左边界,则a-1不可能存在于数组中;如果存在,a-1就是左边界了。
所以若一个元素值a满足:a-1不在数组中,则该元素值a就可以当成一个连续序列的左边界。
要利用一个可以快速查找的数据结构来存储nums中的元素,并且遍历(因为set在存储时有去重的功能,所以运行时间比list快)。

遍历set时,取出元素a: 若a-1存在于set中,则continue;a-1不存在,则a是一个左边界,继续找a+1,a+2…,并记录长度。

import java.util.*;

/**
 首先找到nums中有哪些元素可以当作连续序列的左边界。
 假设a为一个连续序列的左边界,则a-1不可能存在于数组中;如果存在,a-1就是左边界了。
 所以若一个元素值a满足:a-1不在数组中,则该元素值a就可以当成一个连续序列的左边界。
 要利用一个可以快速查找的数据结构来存储nums中的元素,并且遍历(因为set在存储时有去重的功能,所以运行时间比list快)

 遍历set时,取出元素a: 若a-1存在于set中,则continue;a-1不存在,则a是一个左边界,继续找a+1,a+2...,并记录长度
 */
class Solution {
    public int longestConsecutive(int[] nums) {
        int n = nums.length;
        if(n==0)
            return 0;
        
        HashSet<Integer> set = new HashSet<>();
        for(int i=0; i<n; i++) {
            set.add(nums[i]);
        }

        int max = 0;
        for(int a : set) {
            if(set.contains(a-1)){
                continue;
            } else {
                int len = 1;
                while(set.contains(++a)){
                    len++;
                }
                max = Math.max(len, max);
            }
        }
        return max;
    }
}

(129)求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。每条从根节点到叶节点的路径都代表一个数字。
例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。计算从根节点到叶节点生成的 所有数字之和 。叶节点 是指没有子节点的节点。

思路: 理解了这句 preOrder(root.left, num x 10) 中的 num*10,下面代码就懂了。

/**
 * 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 {
    int res = 0;
    public int sumNumbers(TreeNode root) {
        preOrder(root, 0);
        return res;

    }

    public void preOrder(TreeNode root, int num) {
        if(root==null){
            return ;
        }
        num += root.val;
        if(root.left==null && root.right==null){
            res += num;
            return ;
        }
        preOrder(root.left, num*10);
        preOrder(root.right, num*10);
    }
}

(133)克隆图

给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。

class Node {
public int val;
public List neighbors;
}

测试用例格式:
简单起见,每个节点的值都和它的索引相同。例如,第一个节点值为 1(val = 1),第二个节点值为 2(val = 2),以此类推。该图在测试用例中使用邻接列表表示。
邻接列表 是用于表示有限图的无序列表的集合。每个列表都描述了图中节点的邻居集。
给定节点将始终是图中的第一个节点(值为 1)。你必须将 给定节点的拷贝 作为对克隆图的引用返回。

提示:
节点数不超过 100 。
每个节点值 Node.val 都是唯一的,1 <= Node.val <= 100。
无向图是一个简单图,这意味着图中没有重复的边,也没有自环。
由于图是无向的,如果节点 p 是节点 q 的邻居,那么节点 q 也必须是节点 p 的邻居。
图是连通图,你可以从给定节点访问到所有节点。

思路:图也是经常用DFS,BFS的;还有拓扑排序判断有没有循环依赖、环的问题,并查集。

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> neighbors;
    public Node() {
        val = 0;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val) {
        val = _val;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val, ArrayList<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
}
*/
import java.util.*;

class Solution {
    // 这道题就是遍历整个图,但是要记录哪些已经被访问过了,所以用一个字典来记录哪些已经被访问过
    // BFS  或  DFS 都是可以。后面图的遍历都和这个相似,要重点掌握啊。

    public Node cloneGraph(Node node) {
        // DFS
        // Map<Node, Node> lookup = new HashMap<>();
        // return dfs(node, lookup);

        // BFS
        if(node==null)
            return null;
        Map<Node, Node> lookup = new HashMap<>();
        Node clone = new Node(node.val, new ArrayList<>());
        // 不论是bfs还是dfs,lookup都是起到一个记录的作用,记录这个node有没有被访问过,访问过这样做,没访问过就那样做
        lookup.put(node, clone);
        Queue<Node> que = new LinkedList<>();
        que.offer(node);
        while(!que.isEmpty()){
            Node tmp = que.poll();
            for(Node n : tmp.neighbors){
                if(!lookup.containsKey(n)){
                    lookup.put(n, new Node(n.val, new ArrayList<>()));
                    que.offer(n);
                }
                lookup.get(tmp).neighbors.add(lookup.get(n));
            }
        }
        return clone;

        
    }

    public Node dfs(Node node, Map<Node, Node> lookup) {
        if(node==null)
            return null;
        // 如果node这个节点已经被遍历过,则从lookup中返回node的克隆节点
        if(lookup.containsKey(node))
            return lookup.get(node);
        // 如果node这个节点还没被遍历过,则创建node的克隆节点,并把node和其克隆节点放入lookup
        Node clone = new Node(node.val, new ArrayList<>());
        lookup.put(node, clone);
        // 设置clone节点的neighbors
        for(Node n : node.neighbors){
            clone.neighbors.add(dfs(n, lookup));
        }
        return clone;
    }
}

(136)只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

class Solution {
    public int singleNumber(int[] nums) {
        int len = nums.length;
        int res = 0;
        for(int i=0; i<len; i++){
            res ^= nums[i];
        }
        return res;
    }
}

(137)只出现一次的数字-2

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

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

示例 2:
输入:nums = [0,1,0,1,0,1,99]
输出:99

提示:
1 <= nums.length <= 3 * 10^4;
-2^31 <= nums[i] <= 2^31 - 1;
nums 中,除某个元素仅出现 一次外,其余每个元素都恰出现三次;

进阶:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

思路:看代码注释

import java.util.*;

class Solution {
    public int singleNumber(int[] nums) {
        // 下面是Map方法

        // 使用hashmap,key为nums中元素,value为元素出现次数
        // Map<Integer, Integer> map = new HashMap<>();
        // for(int i=0; i<nums.length; i++) {
        //     if(map.containsKey(nums[i])){
        //         map.put(nums[i], 1+map.get(nums[i]));
        //     } else {
        //         map.put(nums[i], 1);
        //     }
        // }

        // for(Map.Entry<Integer, Integer> entry : map.entrySet()){
        //     if(entry.getValue()==1)
        //         return entry.getKey();
        // }
        // Set<Integer> set = map.keySet();
        // for(Integer i : set){
        //     if(map.get(i)==1)
        //         return i;
        // }
        // return -1;


        // 下面是位运算方法
        // 需要统计所有数字在某一位置的和能不能被3整除,不能被3整除,说明那个只出现一次的数字的二进制表示在那个位置位1,
        // 把所有int的32位全部统计一次

        int res = 0; // 0的所有位都是0
        int bitLength = 32; // java中int是32位
        for(int i=0; i<bitLength; i++){
            int oneCount = 0;
            for(int j=0; j<nums.length; j++){
                oneCount += (nums[j] >>> i) & 1;
            }
            if(oneCount%3==1){
                res |= (1 << i);
            }
        }
        return res;
    }
}
/*
该题扩展如下:
一:只有一个数字出现一次,其他数字都出现偶数次,要求这个出现一次的数,只需把所有数字都异或一遍即可。因为:
    1. 自己和自己异或是0;
    2. 0和任何数异或还是任何数自己
    3. 异或满足交换律
二:只有一个数字出现一次,其他都出现奇数次,则用上面的代码,把3换成对应的数即可;
*/

(138)赋值带随机指针的链表

思路:看剑指offer总结中的深度克隆那道题,一模一样的。

(144)二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

/**
 * 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;
 *     }
 * }
 */
import java.util.*;
class Solution {
    // 迭代解法,本质上是模拟递归,因为在递归过程中使用了系统栈,所以在迭代时常用Stack来模拟系统栈

    /*
    前序遍历:首先我们应该创建一个stack用来存放节点,并且第一个应该访问根节点,但此时stack为空,所以先将
    头节点放入stack,然后打印;
    之后我们应该先访问左子树,在访问右子树,所以就应该先向stack中压入右子树,然后压入左子树。因为stack中,
    出栈顺序和入栈顺序相反,而出栈顺序是我们的访问顺序。
    */
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root==null) return res;

        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode tmp = stack.pop();
            res.add(tmp.val);
            if(tmp.right!=null) stack.push(tmp.right);
            if(tmp.left!=null) stack.push(tmp.left);
        }
        return res;
    }
}
/*
中序遍历的迭代思路:
也是借助一个stack,当栈不空或者cur不为null时就一直while循环下去;在循环内部,左子树不空则尽可能地把左子树压入stack,
如果左子树为空就弹出栈顶,访问它,如果它的右子树不为空则右子树入栈,开启while的下一次循环
    TreeNode cur = head;
    Stack<TreeNode> stack = new Stack<>();
    while(!stack.isEmpty() || cur!=null){
        while(cur!=null){
            stack.push(cur);
            cur = cur.left;
        }
        TreeNode node = stack.pop();
        访问node;
        if(node.right!=null){
            cur = node.right;
        }
    }
*/
/*
后续遍历迭代思路(先看下上面的前序遍历迭代法):
    1. 前序遍历的过程是 中左右;
    2. 将前序的访问过程修改为  中右左。也就是压栈的过程中优先压入左子树,再压入右子树;
    3. 然后将  中右左   的访问顺序颠倒过来就是左右中,利用另一个栈的先进后出来颠倒顺序。
    Stack<TreeNode> stack1 = new Stack<>();
    Stack<TreeNode> stack2 = new Stack<>();
    stack1.push(head);
    while(!stack1.isEmpty()){
        TreeNode node = stack1.pop();
        stack2.push(node);
        if(node.left!=null) stack1.push(node.left);
        if(node.right!=null) stack1.push(node.right);
    }
    while(!stack2.isEmpty()){
        TreeNode node = stack2.pop();
        访问node;
    }
*/

(145)二叉树的后序遍历

给定一个二叉树,返回它的 后序 遍历。

/**
 * 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;
 *     }
 * }
 */
import java.util.*;

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root==null) return res;
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        stack1.push(root);
        while(!stack1.isEmpty()){
            TreeNode node = stack1.pop();
            stack2.push(node);
            if(node.left!=null) stack1.push(node.left);
            if(node.right!=null) stack1.push(node.right);
        }
        while(!stack2.isEmpty()){
            TreeNode node = stack2.pop();
            res.add(node.val);
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值