Hot100(二)

12月2日

152. 乘积最大子数组

1.暴力

以每个值为起点,记录从每个元素开始往后相乘出现的最大值。

public static int maxProduct(int[] nums) {
        int len= nums.length;
        int max=nums[0];
        int cur=nums[0];
         for (int i = 0; i < len; i++) {
              cur=nums[i];
              max=cur>max?cur:max;
            for (int j = i+1; j < len; j++) {
                cur=cur*nums[j];
                max=cur>max?cur:max;
            }
        }
        return max;
    }

2.动态规划

题解链接

   public static int maxProduct(int[] nums) {
        int max=Integer.MIN_VALUE,imax=1,imin=1;
        //如果当前元素为负数,max和min互换
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]<0){
                int temp=imax;
                imax=imin;
                imin=temp;
            }
            imax=Math.max(imax*nums[i],nums[i]);
            imin=Math.min(imin*nums[i],nums[i]);
            max=Math.max(max,imax);
        }
        return max;
    }

I

12月3日

155. 最小栈

LinkedList 是一个继承于AbstractSequentialList的双向链表。
LinkedList 可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,所以能对它进行队列操作。
LinkedList 实现 Deque 接口,能将LinkedList当作双端队列使用。

  • stack是堆栈,继承自Vector,没有迭代器,特点是后进先出。用push()将元素压入栈中,top()返回栈顶元素,pop()移除栈顶元素。Stack设计不好的地方,既然只是为了实现栈,不用链表来单独实现。
  • queue是队列,特点是先进先出,不支持迭代器,使用push()将元素排入对中,front()返回队首元素,pop()移除队首元素。
  • Java中的deuqe,即“double ended queue”的缩写,是Java中的双端队列集合类型,它集成自queue,具备普通队列FIFO的功能,同时它也具备了Stack的LIFO功能,并且保留了push和pop函数.

注意:

实际在Java Doc里是建议用Deque替代Stack接口完成栈的功能 。

Java中的Deque和Stack的内部原理区别 | 白发川的BLOG

这个题的做法,有点迷 

题解

/**
 * 建议用deque替代stack,因此stack继承自vector
 * deque继承自queue,是双端队列,用linkedlist实现deque和stack(即FIFO又LIFO)
 **/
class MinStack {
        Deque<Integer> xStack ;
        Deque<Integer> minStack;
        public MinStack() {

            xStack = new LinkedList<Integer>();
            minStack = new LinkedList<Integer>();
            minStack.push(Integer.MAX_VALUE);
        }

//        此写法也可,但建议用Deque替代Stack接口完成栈的功能
//        Stack<Integer> xStack ;
//        Stack<Integer> minStack;
//        public MinStack() {
//            xStack = new Stack<Integer>();
//            minStack = new Stack<Integer>();
//            minStack.push(Integer.MAX_VALUE);
//        }

        public void push(int val) {
            //将元素 x 推入栈中
            xStack.push(val);
            minStack.push(Math.min(minStack.peek(),val));

        }

        public void pop() {
            // 删除栈顶的元素
            xStack.pop();
            minStack.pop();
        }

        public int top() {
            //获取栈顶元素
            return xStack.peek();
        }

        public int getMin() {
            return minStack.peek();
        }
    }

 160. 相交链表

 可以理解成两个人速度一致, 走过的路程一致。那么肯定会同一个时间点到达终点。如果到达终点的最后一段路两人都走的话,那么这段路上俩人肯定是肩并肩手牵手的。

若相交,链表A: a+c, 链表B : b+c。a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。

若不相交,a +b = b+a 。因此相遇处是末尾的NULL。

如果两链表相交,则走过的总路程为a+b,速度相同,路程相同,因此会同一时间到达终点,并且同一时间到达公共起点。

 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA==null || headB==null) return null;
        ListNode preA=headA;ListNode preB=headB;
        while (preA!=preB){
            preA=preA==null?headB:preA.next;
            preB=preB==null?headA:preB.next;
        }
        return preA;
    }

12月6日

169. 多数元素

方法一:不够优化 

//可通过,但需要简化
public static int majorityElement(int[] nums) {
        int len=nums.length;
        Arrays.sort(nums);
        int curVal=nums[0];
        int cruLen=1;
        if (len==1) return curVal;
        for (int i = 0; i < len-1; i++) {
            if (nums[i] == nums[i + 1]) {
                cruLen++;
                //curVal=nums[i];
            }
            if (cruLen > (int) len / 2) {
                return nums[i];
            }
        }
        return -1;
    }

方法二:

 题解

由于多数是出现次数 大于 ⌊ n/2 ⌋ 的元素,因此我们只需要返回排序后的 第⌊ n/2 ⌋元素即可

public static int majorityElement(int[] nums) {
    Arrays.sort(nums);
    return nums[nums.length/2];
}

 方法三:哈希表

由于Map中存放的元素均为键值对,故每一个键值对必然存在一个映射关系。
Map中采用Entry内部类来表示一个映射项,映射包括Key和Value。
Map.Entry里面包含getKey()和getValue()

Set<Entry<T,V>>entrySet()还方法返回值就是这个map中各个键值对映射关系的集合,可使用它对map进行遍历。

public static Map<Integer, Integer> countMap(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        int len = nums.length;
        //统计各个元素出现的次数
        for (int i = 0; i < len; i++) {
            if (map.containsKey(nums[i])){
                map.put(nums[i],map.get(nums[i])+1);
            }else {
                map.put(nums[i],1);
            }
        }
        return map;
    }
    
  public static int majorityElement(int[] nums) {
      Map<Integer, Integer> map =countMap(nums);
      Map.Entry<Integer,Integer> maxEntry =null;
      for (Map.Entry<Integer,Integer> entry:map.entrySet()) {
        if (maxEntry==null || entry.getValue()>maxEntry.getValue()){
            maxEntry=entry;
        }
      }
      return maxEntry.getKey();
  }

198. 打家劫舍

题解

动态规划

public static int rob(int[] nums) {

        if (nums==null || nums.length==0)return -1;
        int len = nums.length;
        if (len==1)return nums[0];

        int[] dp = new int[len];
        dp[0]=nums[0];
        dp[1]=Math.max(nums[0],nums[1]);
        for (int i = 2; i < len; i++) {
            dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[len-1];
    }

使用滚动数组:

public static int rob(int[] nums) {

        if (nums==null || nums.length==0)return -1;
        int len = nums.length;
        if (len==1)return nums[0];
        //滚动数组
        int pre0=nums[0];
        int pre1=Math.max(nums[0],nums[1]);
        for (int i = 2; i < len; i++) {
            int temp=pre1;
            pre1=Math.max(pre0+nums[i],pre1);
             pre0=temp;
        }
        return pre1;
       
    }

12月7日

200. 岛屿数量

题解链接

int[][] direction={{-1,0},{1,0},{0,-1},{0,1}};//上下左右四个方向
    private int m,n;
    public int numIslands(char[][] grid) {
        m=grid.length;
        n=grid[0].length;
        int islandNum=0;
        if (grid==null ||m==0 || n==0) return 0;
        for (int i = 0; i < m; i++) {//竖着遍历
            for (int j = 0; j < n; j++) {
                if (grid[i][j]=='1'){
                    dfs(grid,i,j);
                    islandNum++;
                }
            }
        }
        return islandNum;
    }
   
    public void dfs(char[][] grid,int row,int col){
        
        if (row<0 || row>=m || col<0 ||col>=n || grid[row][col]=='0'){
            return;
        }
        grid[row][col]='0';
        for (int[] dir: direction) {
            dfs(grid,row+dir[0],col+dir[1]);
        }
    }

206. 反转链表

     public ListNode reverseList(ListNode head) {
        if (head==null) return null;
        ListNode cur=head.next;
        ListNode pre=null;
        ListNode newList=new ListNode(head.val);
        while (cur!=null){
            pre=cur.next;
            cur.next=newList;
            newList=cur;
            cur=pre;
        }

return newList;
    }

12月8日

207. 课程表

题解链接

拓扑排序判断是否是有向无环图。

java.util.ArrayDeque.poll() 此方法检索并删除此双端队列表示的队列的头部,如果此双端队列为空,则返回null。

/**
 * @description :因为题目中说[1, 2]的意思是要学1,先得学2,所以是2->1
 * [3, 2] [4,2] : 2->3 2->4
 * adjacency[2]存的就是3,4(即2指向的元素)
 *
 * 参考答案
 **/
public static  boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] indegrees=new int[numCourses];//用于存储每个点(数字)的入度
        List<List<Integer>> adjacency=new ArrayList<>();//邻接表的准备
        Queue<Integer> queue=new LinkedList<>();
        for(int i=0;i<numCourses;i++){
            adjacency.add(new ArrayList<>());
        }//事先创建空的arraylist
        for(int[]cp:prerequisites){
            indegrees[cp[0]]++;//cp[0]这个数前面连接了一个数cp[1],所以度+1
            adjacency.get(cp[1]).add(cp[0]);//创建连接表,在cp[1]对应的位置,放入后连接的一个数字也就是cp[0],
            //一个cp[1]可能对应多个cp[0],例如 [3, 2] [4,2] : 2->3 2->4
        }
        for(int i=0;i<numCourses;i++){
            if(indegrees[i]==0)//如果入度为0入队列
                queue.add(i);
        }
        while(!queue.isEmpty()){
            int pre=queue.poll();//弹出度为0的数,其实也就是准备删除
            numCourses--;//删除后个数减少1,对应作者的图可以理解
            for(int cur:adjacency.get(pre)){//找到入度0连接后面的数字们遍历,例如遍历上面2后连接的3,4
                if(--indegrees[cur]==0){//并且将他们的入度同时-1,如果此时度为0,继续入队列
                    queue.add(cur);
                }
            }
        }
        return numCourses==0;
    }

208. 实现 Trie (前缀树)

题解

题解链接1

 字典树:

class Trie {
        private  Trie[] children;
        private boolean isEnd;
        public Trie() {
            children =new Trie[26];
            isEnd=false;
        }
        public void insert(String word) {
            Trie trie =this;
            for (int i = 0; i < word.length(); i++) {
                char ch = word.charAt(i);
                if(trie.children[ch-'a']==null){
                    trie.children[ch-'a']=new Trie();
                }
                trie=trie.children[ch-'a'];
            }
             trie.isEnd=true;
        }
        //不为空,并且是单词结尾
        public boolean search(String word) {
            Trie node=searchPrefit(word);
            return node!=null && node.isEnd;
        }
        //不为空即可
        public boolean startsWith(String prefix) {
            return searchPrefit(prefix)!=null;
        }
        public Trie searchPrefit(String prefix){
            Trie node =this;
            for (int i = 0; i < prefix.length(); i++) {
                if (node.children[prefix.charAt(i)-'a']==null){
                    return null;
                }
                node=node.children[prefix.charAt(i)-'a'];
            }
            return node;
        }
    }

12月24日

215. 数组中的第K个最大元素

题解思路

队列的add()方法和offer()方法的区别:

两者都是往队列尾部插入元素,不同的时候,当超出队列界限的时候,add()方法是抛出异常让你处理,而offer()方法是直接返回false

方法一:大顶堆

public static int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        PriorityQueue<Integer>  priorityQueue= new PriorityQueue<>(len,(a,b)->(b-a));//大顶堆
        for (int i = 0; i < len; i++) {
            priorityQueue.add(nums[i]);
        }
        for (int i = 0; i < k-1; i++) {
            priorityQueue.poll();
        }
        return priorityQueue.peek();
    }

方法二:小顶堆(空间复杂度低)

小根堆最多只需要k个元素在堆里,因此时间复杂度是 O(nlogk),大顶堆需要len个元素,因此使用k个元素的小顶堆,复杂度更低。

public static int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, (a, b) -> (a - b));//小顶堆
        for (int i = 0; i < k; i++) {
            priorityQueue.add(nums[i]);
        }
        for (int i = k; i <len ; i++) {
            int top=priorityQueue.peek();//只取值,不弹出
            if (nums[i]>top){
                priorityQueue.poll();
                priorityQueue.add(nums[i]);
            }
        }
        return priorityQueue.peek();
    }

方法三:自己实现大顶堆

参考链接

public static int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        int headSize=len;
        buildMaxTree(nums,headSize);
        for (int i = len-1; i >=len-k+1 ; i--) {
            swap(nums,0,i);
            headSize--;
            maxHeapify(nums,0,headSize);
        }
        return nums[0];
    }

    private static void buildMaxTree(int[] nums, int headSize) {
        for (int i = headSize/2; i >=0; i--) {
            maxHeapify(nums,i,headSize);
        }
    }

    private static void maxHeapify(int[] nums,int index,int headSize){
        int left=index*2+1;int right =index*2+2;int largest=index;
        if (left<headSize && nums[left]>nums[largest]){//写为largest
            largest=left;
        }
        if (right<headSize && nums[right]>nums[largest]){
            largest=right;
        }
        if (largest!=index){
            swap(nums,largest,index);
            maxHeapify(nums,largest,headSize);
        }
    }
    private static void swap(int[] arr, int a,int b) {
        int temp=arr[a];
        arr[a]=arr[b];
        arr[b]=temp;
    }

 221. 最大正方形

题解

方法一:动态规划

 

public int maximalSquare(char[][] matrix) {
        if (matrix==null || matrix.length==0 || matrix[0].length==0) return 0;
        int row=matrix.length;int col=matrix[0].length;
        int maxSize=0;
        int dp[][]=new int[row][col];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (matrix[i][j]=='1' ){
                    if (i==0 || j==0) {
                        dp[i][j]=1;
                    }else {
                        dp[i][j]=Math.min(Math.min(dp[i-1][j-1],dp[i-1][j]),dp[i][j-1])+1;
                    }
                    maxSize=Math.max(maxSize,dp[i][j]);
                }
            }
        }
        return maxSize*maxSize;
    }

12月26日

 226. 翻转二叉树

题解链接

此题解是先交换左右叶子节点,再交换左右子树。

   public TreeNode invertTree(TreeNode root) {
      if (root==null) return null;
      TreeNode left  =invertTree(root.left);
      TreeNode right =invertTree(root.right);
      root.left=right;
      root.right=left;
      return root;
    }

也可以先交换左右子树,再交换左右节点。

题解链接

    public TreeNode invertTree(TreeNode root) {
        if (root==null) return null;
        TreeNode temp= root.left;
        root.left=root.right;
        root.right=temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

234. 回文链表

方法一:将值复制到数组中后用双指针法

public boolean isPalindrome(ListNode head) {
        List<Integer> array = new ArrayList<Integer>();
        if (head == null) return true;
        ListNode cur = head;
        while (cur != null) {
            array.add(cur.val);
            cur = cur.next;
        }
        int len = array.size();
        for (int i = 0, j = len - 1; i < len / 2 && j >= len / 2; i++, j--) {
            if (array.get(i) != array.get(j)) {
                return false;
            }
        }
        return true;
    }

快慢指针,都从head开始,slow返回的是中间(奇)或者中间元素的前一个(偶)

举例:若有三个元素返回的是第二个元素,若有四个元素返回的是第二个元素。

12月28日

236. 二叉树的最近公共祖先

题解

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
         //p或q为根节点,则返回root,不能比其更深了
        if (root==null || root == p || root==q){
            return root;
        }
        //q,p都不是根节点,则分别在左右子树中寻找q,p
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        //左右子树中都没有p,q
        if (left==null && right==null){
            return null;
        }
        //左子树中没有q,也没有p,在右子树中,则返回右子树的结果
        if (left==null){
            return right;
        }
        //右子树中没有,在左子树中,则返回左子树的结果
        if (right==null){
            return left;
        }
        //左右子树都有值,则q和p分别在左右子树上
        return root;

    }

238. 除自身以外数组的乘积

 不能使用除法是因为如果有的元素为0,则程序会报错。

题解

方法一:

用num[i]为分界点,分为前缀和后缀。

public int[] productExceptSelf(int[] nums) {
        int[] output = new int[nums.length];
        output[0]=1;
        for (int i = 1; i < nums.length; i++) {
            output[i]=nums[i-1]*output[i-1];
        }
        int R=1;
        for (int i = nums.length-1; i >=0 ; i--) {
            output[i]=output[i]*R;
            R=R*nums[i];
        }
        return output;
        }

12月29日
239. 滑动窗口最大值

public E peek():检索但不删除此列表的头部(第一个元素)。

public E peekFirst():检索但不删除此列表的第一个元素,如果此列表为空,则返回null

public E peekLast():检索但不删除此列表的最后一个元素,如果此列表为空,则返回null

题解

    //加入队尾,
        public static int[] maxSlidingWindow(int[] nums, int k) {
            LinkedList<Integer> queue = new LinkedList<>();
            if (nums==null || nums.length<=1){
                return nums;
            }
            int[] ans = new int[nums.length-k+1];
            for (int i = 0; i < nums.length; i++) {
                //保证从大到小,前面的数小则先弹出
                while (!queue.isEmpty() && nums[queue.peekLast()]<=nums[i]){
                    queue.pollLast();
                }
                queue.addLast(i);//队尾进
                //不在窗口内,弹出
                if (queue.peek()<i+1-k){
                    queue.poll();
                }
                //当前长度大于窗口长度,就可以填入最大值了(加一的原因是i从零开始)
                if (i+1>k){
                    ans[i+1-k]= nums[queue.peek()];
                }
            }
        return ans;
        }

12月30日

240. 搜索二维矩阵 II

方法一:直接查找

    public static boolean searchMatrix(int[][] matrix, int target) {
        int row= matrix.length;int col=matrix[0].length;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (matrix[i][j]==target){
                return true;
                }
            }
        }
        return false;
    }

方法二:每行二分查找

    public static boolean searchMatrix(int[][] matrix, int target) {
        for (int[] i:matrix) {
             int index = seach(i, target);
             if (index>=0){return true;}
        }
        return false;
    }

    private static int seach(int[] nums, int target) {
        int low = 0;
        int high = nums.length - 1;
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

方法三:Z型查找

题解

public static boolean searchMatrix(int[][] matrix, int target) {
        int row= matrix.length;int col=matrix[0].length;
        int i=0,j=col-1;//右上角
        while (i<row && j>=0){
            if (matrix[i][j]==target) return true;
            else if (matrix[i][j]< target){
                //此行不要了,下移一行
                i++;
            }else if (matrix[i][j]> target){
                j--;//此列不要了,左移一列
            }
        }
        return false;
    }

279. 完全平方数

方法:动态规划

题解

 public int numSquares(int n){
            int[] dp=new int[n+1];
            for (int i = 1; i <= n; i++) {
                dp[i]=i;//全是1,例如3=1+1+1
                for (int j = 1; i-j*j>=0 ; j++) {
                    dp[i]=Math.min(dp[i],dp[i-j*j]+1);
                }
            }
            return dp[n];
        }

283. 移动零

public static void moveZeroes(int[] nums) {
        int zeor=0;
        //统计0的个数,并且遇到几个零,就往前移动几位
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]==0){
                zeor++;
                continue;
            }
            if (i>0) nums[i-zeor]=nums[i];

        }
        //末尾赋0
        for (int i = nums.length-1; i > nums.length-1-zeor ;i--) {
            nums[i]=0;
        }
    }

287. 寻找重复数

方法一:空间复杂度为O(n) 

//空间复杂度为O(n) 
  public int findDuplicate(int[] nums) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])){
                map.put(nums[i],1);
            }else return nums[i];
        }
        return -1;
    }

方法二:改变了数组

//先排序,若相邻两个元素值相等,则返回  
public int findDuplicate(int[] nums) {
        Arrays.sort(nums);
        for (int i = 0; i < nums.length-1; i++) {
            if (nums[i]==nums[i+1]){
                return nums[i];
            }
        }
        return -1;
    }

//或者
public int findDuplicate(int[] nums) {
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]==i){
                return nums[i];
            }
        }
        return -1;
}

方法三:(推荐)

空间复杂度为:O(1),且不改变原数组。

快慢指针在入环点相遇: 

设:从起点到环的入口的步数是 a,slow从环的入口继续走 b 步到达相遇位置,从相遇位置继续走 c 步回到环的入口

 参考

 因此先找到相遇点,让慢指针从起点开始, 让快指针从相遇点开始。 这种情况下,想让快指针想要追上慢指针,就是慢指针走a步,快慢指针就在环的入口相遇(即再次相遇的点就是环入口)。

    //快慢指针,初始化都从0开始
    public int findDuplicate(int[] nums) {
        int fast=0,slow=0;
        slow=nums[slow];//慢指针走一步
        fast=nums[nums[fast]];//快指针走两步
        while (slow!=fast){
            slow=nums[slow];
            fast=nums[nums[fast]];
        }
        //让慢指针从起点开始,快指针从相遇点开始,都只走一步
        int pre=slow;
        int pre2 =0;
        while (pre!=pre2){
            pre=nums[pre];
            pre2=nums[pre2];
        }
        return pre;
    }

12月31日

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

题解

  // fp[i][0]: 手上持有股票的最大收益
  // fp[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
   // fp[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益   
 public int maxProfit(int[] prices) {
        int len =prices.length;
        int[][] fp = new int[len][3];
         fp[0][0]=-prices[0];
//         fp[0][0]=0;fp[0][1]=0;//默认为0
        for (int i = 1; i < len; i++) {
            fp[i][0]=Math.max(fp[i-1][0],fp[i-1][2]-prices[i]);
            fp[i][1]=fp[i-1][0]+prices[i];
            fp[i][2]=Math.max(fp[i-1][1],fp[i-1][2]);
        }
        return Math.max(fp[len-1][1],fp[len-1][2]);
    }

public int maxProfit(int[] prices) {
    int len =prices.length;
    int f0=-prices[0];
    int f1=0,f2=0;
    for (int i = 1; i < len; i++) {
        int new0=Math.max(f0,f2-prices[i]);
        int new1=f0+prices[i];
        int new2=Math.max(f1,f2);
        f0=new0;
        f1=new1;
        f2=new2;
    }
    return Math.max(f1,f2);
}

322. 零钱兑换

动态规划

题解

  public int coinChange(int[] coins, int amount) {
        
        int[] dp = new int[amount+1];//dp[i]放入的是i需要几个硬币
        //dp[i]=dp[m]+dp[i-m];
        Arrays.fill(dp,amount+1);
        dp[0]=0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (coins[j]<=i){
                    dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }
        return dp[amount]>amount?-1:dp[amount];
    }



337. 打家劫舍 III

 

class Solution {
    HashMap<TreeNode, Integer> f = new HashMap<>();
    HashMap<TreeNode, Integer> g = new HashMap<>();

    public int rob(TreeNode root) {
        dfs(root);
        return Math.max(f.getOrDefault(root,0),g.getOrDefault(root,0));
    }

    public void dfs(TreeNode root){
        if (root==null) return ;
        dfs(root.left);
        dfs(root.right);
        f.put(root,root.val+g.getOrDefault(root.left,0)+g.getOrDefault(root.right,0));
        g.put(root,Math.max(f.getOrDefault(root.left,0), g.getOrDefault(root.left,0))+Math.max(f.getOrDefault(root.right,0), g.getOrDefault(root.right,0)));
    }
}

22年啦

1月2日

560. 和为 K 的子数组

方法一:枚举

    public int subarraySum(int[] nums, int k) {
    int count=0;
        for (int i = 0; i < nums.length; i++) {
            int sum=0;
            for (int j = i; j >=0 ; j--) {
                sum+=nums[j];
                if (sum==k){
                    count++;
                }
            }
        }
        return count;
    }

方法二:前缀和 + 哈希表优化

题解

 pre为现在的总和, map[i][j]意思是,和为i的连续数组,有j组(pre最终结果为nums.length个元素的和,map最后一行的key也是)

    public static  int subarraySum(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0,1);
        int pre=0;
        int count=0;
        for (int i = 0; i <nums.length ; i++) {
            pre+=nums[i];
//            if (map.containsKey(pre-k)){
//                count+=map.get(pre-k);
//            }
            count+=map.getOrDefault(pre-k,0);//注意写的是key,不是map(key)
            map.put(pre,map.getOrDefault(pre,0)+1);
        }
        return count;
    }

437. 路径总和 III

题解

根据560题解,改编

map[i][j]意思是,路径和为i的路径,有j条。

pre是当前遍历的总路径和。

//map[i][j]意思是,路径和为i的路径,有j条
class Solution {
   int pre =0;//路径和
    int count=0;//路径数目
    public int pathSum(TreeNode root, int targetSum) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0,1);
        path(root,targetSum,map);
        return count;
    }


   public void path(TreeNode root, int targetSum, HashMap<Integer, Integer> map){
        if (root==null) return;
        TreeNode curNode=root;
        pre+=curNode.val;
        // if (map.containsKey(pre-targetSum)){
        //     count+=map.get(pre-targetSum);
        // }
        count+=map.getOrDefault(pre-targetSum,0);
        map.put(pre,map.getOrDefault(pre,0)+1);//参数是key,不是map.get(key)
        //遍历左右子树
        path(root.left,targetSum,map);
        path(root.right,targetSum,map);
        //返回上一层节点
        map.put(pre,map.get(pre)-1);
        pre-=curNode.val;
    }
}



338. 比特位计数

题解

方法一: 

    public int[] countBits(int n) {
        int[] ans=new int[n+1];
        for (int i = 0; i <=n; i++) {
           ans[i]=ands(i);
        }
        return ans;
    }

    public int ands(int n){
        int sum=0;
        while(n>0){
            n=n&(n-1);
            sum++;
        }
        return sum;
    }

 方法二:最低有效位(推荐)

 x 除以 2 的余数可以通过 x & 1 得到:

public int[] countBits(int n) {
    int[] ans=new int[n+1];
    for (int i = 1; i <= n; i++) {
        ans[i]=ans[i>>1]+(i&1);
    }
    return ans;
}

 1月3日

394. 字符串解码

题解

 * 左括号之前的字符,栈stack_res。
 * 临时栈stack,遇到“[”则放入,stack.put(括号内字符重复次数multi,左括号之前的字符res),将此分为两个栈stack_multi和stack_res分别记录次数和字符。
 * 数字mutil记录数字。
 * res记录计算当前的字符string类型,res在遇到“[”前,记录的是“["的字符串,遇到“[”res入栈,并且res归为空,res继续记录“]”后的元素。
/**
 *
 * 左括号之前的字符,栈stack_res
 * 临时栈stack,遇到“[”则放入,stack.put(括号内字符重复次数multi,左括号之前的字符res),将此分为两个栈stack_multi和stack_res分别记录次数和字符
 * 数字mutil记录数字
 * res记录计算当前的字符string类型,res在遇到“[”前,记录的是“["的字符串,遇到“[”res入栈,并且res归为空,res继续记录“]”后的元素
 */  
 public String decodeString(String s) {
        LinkedList<Integer> stack_multi = new LinkedList<>();//重复次数
        LinkedList<String> stack_res = new LinkedList<>();//"["前面的字符串
        StringBuffer res = new StringBuffer();//当前字符串
        int multi=0;//记录当前次数
        for (char a: s.toCharArray()) {
            if (a=='['){
                stack_multi.addLast(multi);
                stack_res.addLast(res.toString());
                multi=0;
                res=new StringBuffer();

            }else if (a==']'){
                StringBuffer tem = new StringBuffer();//当前k[]还原后的字符串
                int cur_multi=stack_multi.pollLast();//结果是Integer类型
                for (int i = 0; i < cur_multi; i++) tem.append(res);
                //for (int i = 0; i < stack_multi.pollLast(); i++) tem.append(res);
                res=new StringBuffer(stack_res.pollLast()+tem);//如果在遇到],则重复res
            }
                else if (a>='0' && a<='9') {
                    multi=multi*10+Integer.parseInt(a+"");//参数是String类型
                }
                else res.append(a);
        }
            return res.toString();
    }

399. 除法求值

题解

并查集讲解

   public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        int size = equations.size();
        UnionFind unionFind = new UnionFind(2 * size);
        //第一步,讲变量值与id进行映射
        HashMap<String, Integer> map = new HashMap<>(2 * size);
        int id=0;
        for (int i = 0; i < size; i++) {
            List<String> equation=equations.get(i);
            String var1 = equation.get(0);
            String var2 = equation.get(1);
            if (!map.containsKey(var1)){
                map.put(var1,id++);
            }
            if (!map.containsKey(var2)){
                map.put(var2,id++);
            }
            unionFind.union(map.get(var1),map.get(var2),values[i]);
        }
        //第二步,查询
        int qsize = queries.size();
        double[] res=new double[qsize];
        for (int i = 0; i < qsize; i++) {
            String var1 = queries.get(i).get(0);
            String var2 = queries.get(i).get(1);
            Integer id1 = map.get(var1);
            Integer id2 = map.get(var2);
            if (id1==null || id2==null){
                res[i]=-1.0d;
            }else {
                res[i]= unionFind.isConnected(id1,id2);
            }
        }
            return res;
    }

//并查集
    private class UnionFind{
        private int[] parent;
        private double[] weight;//指向的父结点的权值
        //1.初始化并查集
        public UnionFind(int n){
        this.parent=new int[n];
        this.weight=new double[n];
        for (int i = 0; i < n; i++) {
            parent[i]=i;
            weight[i]=1.0d;
        }
    }
        //2.查找根节点,路径压缩
        public int find(int x){
            if (x!=parent[x]){
                int origin =parent[x];
                parent[x]=find(parent[x]);//路径压缩,遍历过程中的所有双亲结点直接指向根结点,减少后续查找次数
                weight[x]*=weight[origin];
            }
            return parent[x];
        }

       //3. 合并
        public void union(int x,int y,double value){
            int rootX=find(x);//x的父节点
            int rootY=find(y);
            //父节点是同一个,则不需要合并了
            if (rootX==rootY){
                return;
            }
            parent[rootX]=rootY;//让x指向y的父节点
            //计算新的路径距离,y指向父节点的权重,x指向y的权重为value
            weight[rootX]=weight[y]*value/weight[x];
        }

    //查看x,y是否属于一个集合
        public double isConnected(int x,int y){
            int rootX=find(x);
            int rootY=find(y);
            if (rootX==rootY){
                return weight[x]/weight[y];
            }else {
                return -1.0d;
            }
        }
    }

1月4日

406. 根据身高重建队列

题解

根据第一个元素降序排列,第二个元素升序排列,然后再根据k插入第k个位置(假如:前面有三个比它大的,因为降序排列前面都大于它,因此放在p[3],0,1,2位置的三个元素都大于它)
  public int[][] reconstructQueue(int[][] people) {
    //根据第一个元素降序排列,第二个元素升序排列
        Arrays.sort(people, new Comparator<int[]>() {
            @Override
            public int compare(int[] preson1, int[] preson2) {
                //第一个元素不相等时,第一个元素降序
                if (preson2[0]!=preson1[0]) return preson2[0]-preson1[0];
                //第一个元素相等时,第二个元素升序
                else return preson1[1]-preson2[1];
            }
        });
        LinkedList<int[]> list = new LinkedList<>();
        for (int i = 0; i < people.length; i++) {
            if (people[i][1]< list.size()){
                //假如有m个大于第i个人,则将第i个人放在m位置上。因为首元素降序,i面前的人都高于i(先遍历的人都比其高)
            list.add(people[i][1],people[i]);
            }else list.add(list.size(), people[i]);//否则放在列表末尾
        }
        return list.toArray(new int[list.size()][]);
        }

416. 分割等和子集

题解

public boolean canPartition(int[] nums) {
        int sum=0;
        for (int i = 0; i < nums.length; i++) {
            sum+=nums[i];
        }
        //奇数返回false
        if ((sum&1)==1) return false;
        int target=sum/2;
        boolean dp[][] = new boolean[nums.length][target+1];
        if (nums[0]<=target) dp[0][nums[0]]=true;
        for (int i = 1; i < nums.length ; i++) {
            for (int j = 0; j <=target ; j++) {
               //先把上一行先抄下来,再赋值
                dp[i][j]=dp[i-1][j];
                if (j==nums[i]) {
                    dp[i][j]=true;
                    continue;
                }
                if (j>nums[i]){
                    dp[i][j]=dp[i-1][j] || dp[i-1][j-nums[i]];
                }

                }
            }
        return dp[nums.length-1][target];
        }

438. 找到字符串中所有字母异位词

题解

public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list=new LinkedList<>();
    int slen=s.length();int plen=p.length();
    if (slen<plen) return list;
    int schar[]=new int[26];
    int pchar[]=new int[26];
        for (int i = 0; i < plen; i++) {
            pchar[p.charAt(i)-'a']++;
        }
        int left=0;
        for (int right = 0; right < slen; right++) {
            schar[s.charAt(right)-'a']++;
            //大于说明,当前遍历的出现了p中未出现的字符或者s中出现的字符次数超过了p中的次数,则左边界右移
            while (schar[s.charAt(right)-'a']>pchar[s.charAt(right)-'a']){
                schar[s.charAt(left)-'a']--;
                left++;
            }
            if (right-left+1==p.length()){
                list.add(left);
            }
        }
        return list;
    }

1月5日

448. 找到所有数组中消失的数字

用map来存储每个元素出现的次数, 空间复杂度为O(n) 

  public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> list = new LinkedList<>();
        Map<Integer, Integer> map = new HashMap<>();
        int len= nums.length;
        Arrays.sort(nums);
        for (int i = 0; i < len; i++) {
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        for (int i = 1; i <= len; i++) {
            if (!map.containsKey(i)){
                list.add(i);
            }
        }
        return list;
    }

方法二:

题解

空间复杂度为O(1)

nums的数组长度为n,我们可以考虑用nums存储,每个元素出现的次数,

因为元素范围是[1,n],则若出现元素k,我们将nums[k-1]+n,最后判断数组元素值,若超过n则说明出现过。num[i]%len取的是数组原来的值(即不加len之前的值)。

public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> list = new LinkedList<>();
        int len= nums.length;
        for (int i = 0; i < len; i++) {
            int x=(nums[i]-1)%len;//因为元素是[1,n],但是数组下标是[0,n-1]
            nums[x]+=len;
        }
        for (int i = 0; i < len; i++) {
            if (nums[i]<=len){
                list.add(i+1);//num[i]存储的是i+1是否存在
            }
        }
        return list;
    }

461. 汉明距离

题解

方法一:内置方法

^是异或,bitcount(i)是计算i的二进制中1的个数。
    public int hammingDistance(int x, int y) {
        //^是异或,bitcount(i)是计算i的二进制中1的个数
        return Integer.bitCount(x^y);
    }

方法二:每次计算异或后二进制最后一位的1,然后右移一位

二进制没有正负,补码有正负

    public int hammingDistance(int x, int y) {
        int s=x^y;
        int count=0;
        //或者while(s!=0)均可
        while(s>0){
            count+=s&1;//计算末尾是否为0
            s=s>>1;
        }
        return count;
    }

方法三:

x&(x-1)是删除最右侧的1。

public int hammingDistance(int x, int y) {
    int s =x^y;
    int count=0;
    while (s!=0){
        s=s&(s-1);//删除最右侧的1
        count++;
    }
    return count;
}

494. 目标和

题解

方法一:回溯

class Solution {
    int count=0;
    public int findTargetSumWays(int[] nums, int target) {
         backing(nums,target,0,0);
        return count;
    }

    private void backing(int[] nums, int target, int index,int cur) {
        if (index==nums.length){
            if (cur==target)count++;
        }else {
            backing(nums,target,index+1,cur+nums[index]);
            backing(nums,target,index+1,cur-nums[index]);
        }
    }
}

方法二:动态规划 

    int sum=0;
    public int findTargetSumWays(int[] nums, int target) {
        int len=nums.length;
        for (int i = 0; i < len; i++) {
             sum+=nums[i];
        }
        int diff=sum-target;
        if (diff<0 || diff%2!=0)return 0;
        int neg=diff/2;
        int[][] dp=new int[len+1][neg+1];//dp[i][j]代表前i个值的和为j的方案数
        dp[0][0]=1;
        for (int i = 1; i <= len; i++) {
            int num=nums[i-1];//前i个值是nums[0,i-1]
            for (int j = 0; j < neg+1; j++) {
                dp[i][j]=dp[i-1][j];//不要第i个值
                if (j>=num){
                    dp[i][j]+=dp[i-1][j-num];
                }
            }
        }
        return dp[len][neg];
    }

1月6日

312. 戳气球

题解视频

题解

 dp[i][j] 表示开区间 (i,j) 内你能拿到的最多金币。

public int maxCoins(int[] nums) {
//给nums两端各加一个1,让数组运算时不越界
        int n=nums.length;
        int temp[]=new int[n+2];
        temp[0]=temp[n+1]=1;
        for (int i = 0; i < n; i++) {
            temp[i+1]=nums[i];
        }
//dp[i][j] 表示开区间 (i,j) 内你能拿到的最多金币
        int dp[][]=new int[n+2][n+2];
        //len表示开区间长度[3,n+2],最短为3,因为两边为开区间
        for (int len = 3; len <=n+2 ; len++) {
            //i表示开区间的左端点[0,n+2-len]
            for (int i = 0; i <= n+2-len; i++) {
                int res=0;
                //k表示开区间内的索引[i+1,i+len-1],因为最右侧可能是临界的1
                for (int k = i+1; k < i+len-1; k++) {
                    int left=dp[i][k];
                    int right=dp[k][i+len-1];
                    res=Math.max(res,left+right+temp[i]*temp[k]*temp[i+len-1]);
                }
                dp[i][i+len-1]=res;
            }
        }
        return dp[0][n+1];
    }

538. 把二叉搜索树转换为累加树

题解

反中序遍历:先遍历右子树,再遍历根节点,最后遍历左子树。

//先遍历右子树,再遍历根节点,最后遍历左子树,反中序遍历
class Solution {
      int sum=0;
    public TreeNode convertBST(TreeNode root) {
        if (root==null) return null;
        convertBST(root.right);
        sum += root.val;
        root.val=sum;
        convertBST(root.left);
        return root;
    }
}

543. 二叉树的直径

题解

 

    int ans=0;
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
       return ans;

    }
    //1——》2——》3(是三层)
    public int depth(TreeNode root){
        if (root==null) return 0;
        int left=depth(root.left);//左子树的深度
        int right=depth(root.right);//右子树的深度
        ans=Math.max(ans,left+right);//最长路径
        return Math.max(left,right)+1;//返回以此root为根的层数
    }

1月9日

581. 最短无序连续子数组

题解

方法一:排序,记录不相同的起始和末尾节点

class Solution {
    public int findUnsortedSubarray(int[] nums) {
        int len=nums.length;
        if (isSort(nums))return 0;
        int[] numsort=new int[len];
        for (int i = 0; i < len; i++) {
            numsort[i]=nums[i];
        }
        Arrays.sort(numsort);
        int left=0;int right=len-1;
        while (numsort[left]==nums[left]){
            left++;
        }
        while (numsort[right]==nums[right]){
            right--;
        }
        return right-left+1;
    }
    public boolean isSort(int[] nums){
        int len=nums.length;
        for (int i = 0; i < len-1; i++) {
            if (nums[i]>nums[i+1]){
                return false;
            }
        }
        return true;
    }
}

 题解1

注意end,start的初始值

如果为升序,最终结果为0,又因为end=-1,所以start要为0;

 public static int findUnsortedSubarray(int[] nums) {
        int len=nums.length;
        int end=-1,start=0;
        int min=nums[len-1],max=nums[0];
        for (int i = 0; i < len; i++) {
            //从左往右找右边界最大值,逆序记录右边界
            if (nums[i] < max) {
                end = i;
            } else {
                //正常情况记录最大值
                max = nums[i];
            }
            //找左边界最小值,逆序时记录左边界
            if (nums[len-1-i]>min){
                start=len-1-i;
            }else {//正常时记录最小值
                min=nums[len-1-i];
            }
        }

//如果为升序,最终结果为0,又因为end=-1,所以start要为0;
        return end-start+1;
    }

617. 合并二叉树

题解

    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if (root1==null) return root2;
        if (root2==null) return root1;
        TreeNode merge=new TreeNode(root1.val+ root2.val);
        merge.left=mergeTrees(root1.left,root2.left);
        merge.right=mergeTrees(root1.right,root2.right);
        return merge;
    }

621. 任务调度器

题解

  public int leastInterval(char[] tasks, int n) {
        int[] ch=new int[26];//记录每个任务出现的次数
        int maxTask=0;//出现最大次数的任务个数(列数)
        int coutMaxTask=0;//(最后一行的列数)
        for (int i = 0; i < tasks.length; i++) {
            ch[tasks[i]-'A']++;
            maxTask=Math.max(maxTask,ch[tasks[i]-'A']);
        }
        for (int i = 0; i < ch.length; i++) {
            if (maxTask==ch[i]){
                coutMaxTask++;
            }
        }
        return Math.max(tasks.length,(maxTask-1)*(n+1)+coutMaxTask);
    }

647. 回文子串

 题解

方法一:动态规划

dp[i][j] 表示 [i,j] 子串是否是回文串,判断该子串是否是回文串需要两个条件:1. s[i] == s[j] 2. 掐头去尾看是否还是回文,即 dp[i+1][j-1] 是否是回文。意思就是 dp[i][j] 需要 dp[i+1][j-1] 来判断,即当前元素需要它的左下角元素来判断。正常我们是 for i 里面套 for j,这样循环是一层一层的,就出现了矛盾,即求 dp[i][j] 的时候 dp[i+1][j-1] 此时还是未知的

综上,所以我们 for j 里面套 for i,这样循环是一列一列的,求 dp[i][j] 的时候 dp[i+1][j-1] 已经知道了.

    public int countSubstrings(String s) {
        // 动态规划法
        boolean[][] dp = new boolean[s.length()][s.length()];
        int ans = 0;

        for (int j = 0; j < s.length(); j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                    ans++;
                }
            }
        }

        return ans;
    }

方法二:中心扩展法

public int countSubstrings(String s) {
        int ans=0;
        for (int center = 0; center < 2*s.length()-1; center++) {
            int left=center/2;
            int right=left+center%2;
            while (left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){
                ans++;
                left--;//从中心扩散,左--,右++
                right++;
            }

        }
        return ans;
    }

方法三:递归

class Solution647 {
    int count=0;
    public int countSubstrings(String s) {
        for (int i = 0; i < s.length(); i++) {
            for (int j = i; j <=i+1 ; j++) {
                backing(s,i,j);//abc,既可以从b开始,也可以从ab开始
            }
        }
        return count;
    }

    private void backing(String s, int left,int right) {
        if (left<0 || right>=s.length()) return;
        if (s.charAt(left)==s.charAt(right)){
            count++;
            backing(s,left-1,right+1);
        }else return;
    }
}

739. 每日温度

方法一:暴力

    public int[] dailyTemperatures(int[] temperatures) {
        int count=0;
        int len=temperatures.length;
        int[] ans=new int[len];
        for (int i = 0; i < len-1; i++) {
            for (int j = i+1; j < len; j++) {
                if (temperatures[j]>temperatures[i]){
                    ans[i]=j-i;
                    break;
                }
            }
        }
        return ans;
    }

方法二:递减栈

public int[] dailyTemperatures(int[] temperatures) {
    int len=temperatures.length;
    Stack<Integer> stack = new Stack<>();
    int[] ans=new int[len];
    //一次遍历,比当前栈顶元素小入栈,编号进栈
    for (int i = 0; i < len; i++) {
        while (!stack.isEmpty() && temperatures[i]>temperatures[stack.peek()]){//取栈顶
            int pre=stack.pop();//出栈
            ans[pre]=i-pre;
        }
        stack.add(i);
    }
        return ans;
}

76. 最小覆盖子串

题解

public static String minWindow(String s, String t) {
        int[] need=new int[128];//t中所需频率
        int left=0,right=0,start=0;
        int minLen=Integer.MAX_VALUE;
        int needCnt=t.length();
        for (int i = 0; i < t.length(); i++) {
            need[t.charAt(i)]++;
        }
        while (right<s.length()){
            char ch=s.charAt(right);
            if (need[ch]>0){
                needCnt--;
            }
            need[ch]--;//放入need数组
            //needCnt等于0,说明t中所有元素被包含进去,则变化左边界
            if (needCnt==0){
                while (need[s.charAt(left)]<0){
                    need[s.charAt(left)]++;//先给need对于位置++,再left++
                    left++;
                }
                //更新最小距离,和记录的起始点
                if (right-left+1<minLen){
                    minLen=right-left+1;
                    start=left;
                }
                need[s.charAt(left)]++;//先变回去,再left++
                left++;
                needCnt++;
            }
            right++;
        }
return minLen==Integer.MAX_VALUE ? "":s.substring(start,start+minLen);
    }

1月12日

   Java 程序员,别用 Stack?! 

 84. 柱状图中最大的矩形

题解

stack存的是下标, 栈是单调递增栈,遇到比其小的都弹出, 比栈顶元素大的才直接入栈。

 若栈顶元素大于等于当前遍历元素,则依次弹出,直到满足条件(当前大于栈顶时停止),更新左边界数组。

注意:刚开始都为右边界赋值len,因此当全为相同元素时,左边界为-1。(当heights[]={5,5,5},则left[]={-1,-1,-1},right[]={0,1,2})

public int largestRectangleArea(int[] heights) {
        int len=heights.length;
        int[] right=new int[len];
        int[] left=new int[len];
        Arrays.fill(right,len);//不要忘记赋值,最后一个元素的右边界
        Deque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < len; i++) {
//严格单调递增栈,
            while (!stack.isEmpty() && heights[stack.peek()]>=heights[i]){
                right[stack.peek()]=i;
                stack.pop();
            }
            left[i]=stack.isEmpty()?-1:stack.peek();
            stack.push(i);
        }
        int ans=0;
        for (int i = 0; i < len; i++) {
            ans=Math.max(ans,(right[i]-left[i]-1)*heights[i]);
        }
        return ans;
    }

85. 最大矩形

题解

将此题转换为柱体,与84题的解法完全一样。 

每更新一层,更新一次最大面积。 

      public int maximalRectangle(char[][] matrix) {
        int row=matrix.length;
        if (row==0) return 0;
        int col=matrix[0].length;
        int maxArea=0;
        int[] height=new int[col];
        //计算高度
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (matrix[i][j]=='1'){
                    height[j]++;
                }else {
                    height[j]=0;
                }
            }
            //位置要放在for循环内,内更新一层计算一次最大面面积
            maxArea=Math.max(maxArea,largestRectangleArea(height));
        }
        return maxArea;
    }

    //84题解
    public int largestRectangleArea(int[] height){
        Deque<Integer> stack = new ArrayDeque<>();
        int len=height.length;
        int[] left=new int[len];
        int[] right=new int[len];
        Arrays.fill(right,len);//不要忘记赋值,最后一个元素的右边界
        
        for (int i = 0; i < len; i++) {
            //栈顶元素大于等于当前元素,弹出(完全单调递增栈)
            while (!stack.isEmpty() && height[stack.peek()]>=height[i]) {
                right[stack.peek()] = i;
                stack.pop();
            }
            left[i] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(i);
        }
        int maxArea=0;
        for (int i = 0; i < len; i++) {
            maxArea=Math.max(maxArea,(right[i]-left[i]-1)*height[i]);
        }
        return maxArea;
    }

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

题解

 

maxGain返回的值,因为要和再上一层的节点相连,只能选当前根节点+左/右(只能选左或者右或者都不选,因为如果是左中右则是闭环,没办法加上一层

左中右的值记录在maxSum中,最终主函数返回的maxSum 

    int maxSum=Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        maxGain(root);
        return maxSum;
    }
    public int maxGain(TreeNode node){
        if (node==null)return 0;
        //如果左右节点最大贡献值为负数,则舍弃(只要根节点,不要左右负节点)
        int leftGain=Math.max(maxGain(node.left),0);
        int rightGain=Math.max(maxGain(node.right),0);
        //节点做大路径取决于,当前节点和左右子树的和
        int lmr= node.val+leftGain+rightGain;//左中右
        maxSum=Math.max(maxSum,lmr);
        //因为要和再上一层的节点相连,只能选当前根节点+左/右(只能选左或者右或者都不选,因为如果是左中右则是闭环,没办法加上一层)
        return node.val+Math.max(leftGain,rightGain);
    }

1月14日

301. 删除无效的括号

20. 有效的括号22. 括号生成

题解

统计要移除括号的个数,再回溯,移除括号(回溯考虑停止条件)。
回溯的时候,把最终结果res不要当参数传递,容易出错,写成全局变量。

class Solution {
     List<String> res = new ArrayList<>();
  public  List<String> removeInvalidParentheses(String s) {
        //1.统计要移除括号的个数
        int lr = 0;//leftRemove移除左括号的个数
        int rr = 0;//rightRemove移除右括号的个数
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') lr++;
            //除了左括号、右括号,还有字母呢
            if (s.charAt(i) == ')') {
                if (lr > 0) lr--;
                else rr++;
            }
        }
        //2.回溯,移除括号
        backTrack(0, lr, rr, s);
        return res;
    }

    //回溯,移除括号
    //回溯的时候,把最终结果res不要当参数传递,容易出错,写成全局变量
    public  void backTrack(int start, int lr, int rr, String s) {
        //完成条件
        if (lr == 0 && rr == 0) {
            if (isVaild(s)) res.add(s);
        }

        for (int i = start; i < s.length(); i++) {
            //重复情况()),此时两个右括号,去掉第一个和第二个结果一样,若都遍历一次,则结果重复
            //if (i < s.length() - 1 && s.charAt(i) == s.charAt(i + 1)) continue;//此处))不通过
            if(i > start && s.charAt(i) == s.charAt(i - 1))continue;
            if (s.charAt(i) == '(' && lr > 0) {
                backTrack(i, lr - 1, rr, s.substring(0, i) + s.substring(i + 1));
            }
            if (s.charAt(i) == ')' && rr > 0) {
                backTrack(i, lr, rr - 1, s.substring(0, i) + s.substring(i + 1));
            }
        }
    }

    //判断是否为有效括号
    public  boolean isVaild(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (int i = 0; i < s.length(); i++) {
            if (!stack.isEmpty() && s.charAt(i) == ')' && stack.peek() == '(') {
                stack.pop();
                continue;
            }
            if (s.charAt(i) != '(' && s.charAt(i) != ')') continue;
            stack.push(s.charAt(i));
        }
        return stack.isEmpty();
    }
}

297. 二叉树的序列化与反序列化

题解

反序列化

public class Codec {
        // Encodes a tree to a single string.
        public String serialize(TreeNode root) {
           if(root==null) return "None,";//可换为任意字符,*、A、X、None
            String leftSerialize=serialize(root.left);
            String rightSerialize=serialize(root.right);
            return root.val+","+leftSerialize+rightSerialize;
        }

        /*按照前序遍历的顺序:先构建根节点,再构建左子树、右子树
         * 将 list 数组的首项弹出,它是当前子树的 root 节点
         * 如果它为 'None' ,返回 null
         * 如果它不为 'None',则为它创建节点,并递归调用 buildTree 构建左右子树,当前子树构建完毕,向上返回
         */
        // Decodes your encoded data to tree.
        public TreeNode deserialize(String data) {
            String[] split = data.split(",");
            Deque<String> deque = new LinkedList<>(Arrays.asList(split));
            return buildTree(deque);
        }
        private TreeNode buildTree(Deque<String> deque){
            String s=deque.poll();
            if (s.equals("None")) return null;
            TreeNode node = new TreeNode(Integer.parseInt(s));
            node.left=buildTree(deque);
            node.right=buildTree(deque);
            return node;
        }
    }

300. 最长递增子序列

 题解

public int lengthOfLIS(int[] nums) {
		if (nums.length==0) return 0;
		int dp[] = new int[nums.length];
		Arrays.fill(dp, 1);
		int res=0;
		for (int i = 0; i < nums.length; i++) {
			for (int j = 0; j < i; j++) {
				if (nums[j] < nums[i]) {
					dp[i]=Math.max(dp[i], dp[j]+1);
				}	
			}
            res=Math.max(res,dp[i]);
		}
		return res;//选出dp中最大的
	}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值