LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

303. 区域和检索 - 数组不可变

给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。

示例:

给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()

sumRange(0, 2) -> 1 sumRange(2, 5) -> -1 sumRange(0, 5) -> -3 说明:

你可以假设数组不可变。 会多次调用 sumRange 方法。

通过前缀和求得0-i的的和
        求得0-j的和
        
        减去就可以得到i-j的和
class NumArray {

      private int[] sums;

    public NumArray(int[] nums) {
        sums = new int[nums.length];
        if (nums.length == 0) {
            return;
        }
        sums[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            sums[i] += sums[i - 1] + nums[i];
        }
    }
    
    public int sumRange(int i, int j) {
        if (i == 0) {
            return sums[j];
        } else {
            return sums[j] - sums[i - 1];
        }
    }
}

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(i,j);
 */

304. 二维区域和检索 - 矩阵不可变

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。 在这里插入图片描述

Range Sum Query 2D 上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。

示例:

给定 matrix = [ [3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7], [1, 0, 3, 0, 5] ]

sumRegion(2, 1, 4, 3) -> 8 sumRegion(1, 1, 2, 2) -> 11 sumRegion(1, 2, 2, 4) -> 12 说明:

你可以假设矩阵不可变。 会多次调用 sumRegion 方法。 你可以假设 row1 ≤ row2 且 col1 ≤ col2。

这个和上面题是类似的,上题是一维的,这里是二维的
用一个数组保存i,0点到i,j的矩阵和
然后求得时候直接循环每一行,
对应矩阵右边-矩阵左边就是这一行矩阵内的范围和
把每一行的加起来

class NumMatrix {

     static int[][] data = new int[1050][1050];
    public NumMatrix(int[][] matrix) {
        if(matrix.length > 0){
            for(int i = 0;i < matrix.length;i++){
                int sum = 0;
                for(int j = 1;j <= matrix[0].length;j++){
                    sum += matrix[i][j - 1];
                    data[i][j] = sum;
                }
            }
        }
    }
    public int sumRegion(int row1, int col1, int row2, int col2) {
        int sum = 0;
        for(int i = row1;i <= row2;i++ ){
            sum += data[i][col2 + 1] - data[i][col1];
        }
        return sum;
    }
}

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix obj = new NumMatrix(matrix);
 * int param_1 = obj.sumRegion(row1,col1,row2,col2);
 */

306. 累加数

累加数是一个字符串,组成它的数字可以形成累加序列。

一个有效的累加序列必须至少包含 3 个数。除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和。

给定一个只包含数字 '0'-'9' 的字符串,编写一个算法来判断给定输入是否是累加数。

说明: 累加序列里的数不会以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。

示例 1:

输入: "112358" 输出: true 解释: 累加序列为: 1, 1, 2, 3, 5, 8 。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8 示例 2:

输入: "199100199" 输出: true 解释: 累加序列为: 1, 99, 100, 199。1 + 99 = 100, 99 + 100 = 199 进阶: 你如何处理一个溢出的过大的整数输入?

题目要求的是两个数相加等于第三个数
检测前一半的就可以
然后第二层当作第二个数的截至点

然后判断前面的数和后面的数加起来是不是剩下的数
isAdditive方法就是判断是否符合当前条件,此方法会一直向后递归寻找的,如果不存在就返回false
class Solution {
    public boolean isAdditiveNumber(String num) {
    if (num.length() < 3) return false;
    int n = num.length();
    for (int i = 1; i <= (n - 1) / 2; i++) {
        if (i > 1 && num.charAt(0) == '0') break;
        for (int j = i + 1; (n - j) >= i && (n - j) >= j - i; j++) {
            if (j - i > 1 && num.charAt(i) == '0') break;
            long num1 = Long.parseLong(num.substring(0, i));
            long num2 = Long.parseLong(num.substring(i, j));
            if (isAdditive(num.substring(j), num1, num2))
                return true;
        }
    }
    return false;
}

private boolean isAdditive(String remain, long num1, long num2) {
    if (remain.equals("")) return true;
    long sum = num1 + num2;
    String str = String.valueOf(sum);
    if (!remain.startsWith(str)) return false;
    return isAdditive(remain.substring(str.length()), num2, sum);
}
}

307. 区域和检索 - 数组可修改

给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。

update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。

示例:

Given nums = [1, 3, 5]

sumRange(0, 2) -> 9 update(1, 2) sumRange(0, 2) -> 8 说明:

数组仅可以在 update 函数下进行修改。 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。

	线段树
        就算法题里面太经典的东西了
        多叉树,字典树,二叉树,线段树

import java.util.function.*;

public class NumArray{

    private SegmentTree<Integer> segTree;

    public NumArray(int[] nums) {
        if (nums.length>0) {
            Integer[] data=new Integer[nums.length];
            for (int i=0;i<nums.length;i++) {
                data[i]=nums[i];
            }
            segTree = new SegmentTree<Integer>(data,(a,b)->a+b);
        }
    }
    
    public void update(int i, int val) {
        segTree.update(i,val);
    }
    
    public int sumRange(int i, int j) {
        return segTree.searchRange(i,j);
    }
}

class SegmentTree<E>{
    
    private E[] data;

    private E[] tree;

    private BiFunction<E,E,E> function;

    public SegmentTree(E[] arr,BiFunction<E,E,E> function){
        data = (E[]) new Object[arr.length];
        this.function=function;
        System.arraycopy(arr,0,data,0,arr.length);
        tree = (E[]) new Object[4*arr.length];
        buildSegmentTree(0,0,arr.length-1);
    }

    //根据传入的BiFuction构建线段树
    private void buildSegmentTree(int index,int left,int right){
        if (left==right) {
            tree[index] =data[right];
            return;
        }
        int leftIndex=leftChild(index);
        int rightIndex=rightChild(index);
        int mid=left+(right-left)/2;
        buildSegmentTree(leftIndex,left,mid);
        buildSegmentTree(rightIndex,mid+1,right);
        //区间数据和,根据业务需求来
        tree[index]=function.apply(tree[leftIndex],tree[rightIndex]);
    }

    //范围搜索
    public E searchRange(int left,int right){
        return searchRange(0,0,data.length-1,left,right);
    }

    private E searchRange(int rootIndex,int left,int right,int targetLeft,int targetRight){
        if (targetLeft == left && targetRight == right) {
            return tree[rootIndex];
        }
        int mid=left+(right-left)/2;
        if (targetLeft>mid) {
            return searchRange(rightChild(rootIndex),mid+1,right,targetLeft,targetRight);
        }
        if (targetRight<=mid) {
            return searchRange(leftChild(rootIndex),left,mid,targetLeft,targetRight);
        }
        return function.apply(searchRange(leftChild(rootIndex),left,mid,targetLeft,mid),searchRange(rightChild(rootIndex),mid+1,right,mid+1,targetRight));
    }


    public void update(int index,E e){
        if (index<0 || index>=data.length) {
            throw new IllegalArgumentException("index illegal");
        }
        update(0,0,data.length-1,index,e);
    }

    public void update(int rootIndex,int left,int right,int targetIndex,E e){
        if (left == right) {
            tree[rootIndex]=e;
            return;
        }
        int mid=left+(right-left)/2;
        if (targetIndex<=mid) {
            update(leftChild(rootIndex),left,mid,targetIndex,e);
        }else{
            update(rightChild(rootIndex),mid+1,right,targetIndex,e);
        }
        tree[rootIndex]=function.apply(tree[leftChild(rootIndex)],tree[rightChild(rootIndex)]);
    }

    //左孩子
    private int leftChild(int index){
        return index*2+1;
    }

    //右孩子
    private int rightChild(int index){
        return index*2+2;
    }
}

309. 最佳买卖股票时机含冷冻期

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 示例:

输入: [1,2,3,0,2] 输出: 3 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        //由于可以无限次交易,所以只定义两个维度,第一个维度是天数,第二个维度表示是否持有股票,0表示不持有,1表示持有,2表示过渡期
        int[][] dp = new int[prices.length][3];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[0][2] = 0;
        for (int i = 1; i < prices.length; i++) {
            //第i天不持有股票的情况有两种
            //a.第i-1天也不持有股票
            //b.第i-1天是过渡期
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2]);
            //第i天持有股票有两种情况
            //a.第i-1天也持有股票,第i天不操作,
            //b.第i-1天不持有股票,在第i天买入
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);
            //第i天是冷冻期只有一种情况,第i-1天持有股票且卖出
            dp[i][2] = dp[i-1][1] + prices[i];
        }
        //最后最大利润为最后一天,不持有股票或者进入冷冻期的情况
        return Math.max(dp[prices.length-1][0], dp[prices.length-1][2]);
    }
}

310. 最小高度树

对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。

格式

该图包含 n 个节点,标记为 0 到 n - 1。给定数字 n 和一个无向边 edges 列表(每一个边都是一对标签)。

你可以假设没有重复的边会出现在 edges 中。由于所有的边都是无向边, [0, 1]和 [1, 0] 是相同的,因此不会同时出现在 edges 里。

示例 1:

输入: n = 4, edges = [[1, 0], [1, 2], [1, 3]]

    0
    |
    1
   / \
  2   3 

输出: [1] 示例 2:

输入: n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]

 0  1  2
  \ | /
    3
    |
    4
    |
    5 

输出: [3, 4] 说明:

根据树的定义,树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。 树的高度是指根节点和叶子节点之间最长向下路径上边的数量。

	我的效率倒数第一
        BFS遍历
class Solution {
     
    private boolean[][] graph;
    private boolean[] visited;
    private int[] e;
    private Queue<Integer> queue;

    
    
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
    
        graph=new  boolean[n][n];
        
        visited=new boolean[n];
        e=new int[n];
        queue=new LinkedList<>();
        //初始化构建图
        for(int i=0;i<edges.length;i++){
            graph[edges[i][0]][edges[i][1]]=true;
            graph[edges[i][1]][edges[i][0]]=true;
            e[edges[i][0]]++;
            e[edges[i][1]]++;
            
        }
        //去除最外层的节点
        while(n>2){
            //遍历图,找到最外层节点
            findOuter();
            while(!queue.isEmpty()){
                Integer v=queue.poll();
                e[v]--;
                n--;
                visited[v]=true;
                for(int i=0;i<graph[v].length;i++){
                    if(graph[v][i]){
                        e[i]--;
                        graph[v][i]=false;
                        graph[i][v]=false;
                    }
                }

                
            }            
        } 
        List<Integer> rt=new ArrayList<>();
        for(int i=0;i<visited.length;i++){
            if(!visited[i]){
                rt.add(i);
            }
        }
        return rt;
    }
    
    public void findOuter(){
        for(int i=0;i<e.length;i++){
            if(e[i]==1){
                queue.add(i);
            }
        }
    }
}