leetcode 100-150题-java版
- (101)对称二叉树
- (102)二叉树的层序遍历
- (103)二叉树的锯齿形层次遍历
- (104)二叉树的最大深度
- (105)从前序和中序遍历构造二叉树
- (106)从中序和后序遍历构造二叉树
- (107)二叉树的层序遍历-2
- (108)将有序数组转化为二叉搜索树
- (109)有序链表转化为二叉搜索树
- (110)平衡二叉树
- (112)路径总和
- (113)路径总和-2
- (116)填充每个节点的下一个右侧结点指针
- (118)杨辉三角
- (119)杨辉三角-2
- (120)三角形中最小路径和
- (121)买卖股票的最佳时机
- (122)买卖股票的最佳时机-2
- (123)买卖股票的最佳时机-3
- (124)二叉树中最大路径和
- (125)验证回文串
- (128)最长连续序列
- (129)求根节点到叶节点数字之和
- (133)克隆图
- (136)只出现一次的数字
- (137)只出现一次的数字-2
- (138)赋值带随机指针的链表
- (144)二叉树的前序遍历
- (145)二叉树的后序遍历
(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;
}
}