HOT100(一)

2021.9月4日

题解链接

1. 两数之和

1.1 暴力

package Hot100;

/**
 * @author : lkw
 * @data : 2021/9/4 9:47
 * @description :暴力
 * 时间复杂度O(n^2)
 **/
public class twoSum01_1 {

    public static void main(String[] args) {
        int[] nums={2,7,11,15};
        int target = 9;
        int[] a=twoSum(nums,target);
        System.out.println(a.toString());
    }
    public static int[] twoSum(int[] nums, int target){
        int[] ans = new int[2];
        for (int i = 0; i < nums.length-1; i++) {
            for (int j = i+1; j < nums.length; j++) {
                if (nums[i]+nums[j]==target){
                    ans[0]=i;
                    ans[1]=j;
                }
            }
        }
        return ans;

    }



}

1.2 hash表(推荐)

//map<key,value>放的是<值,在nums的下标>
public static int[] twoSum(int[] nums, int target){
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        if (map.containsKey(target-nums[i])){
        return new int[] {map.get(target-nums[i]),i};
        }
        map.put(nums[i],i);
    }

    return new int[0];

2. 两数相加

2.1 三个样例未通过,超过long长度)

下次要看清题目的位数,

long long的最大值:9223372036854775807(19位)

long long的最小值:-9223372036854775808

public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//l1 = [2,4,3], l2 = [5,6,4]
        StringBuffer str1 = new StringBuffer();
        StringBuffer str2 = new StringBuffer();
        while (l1!=null){
            str1.insert(0,l1.val);
//            str1.append(l1.val);
            l1=l1.next;
        }
        while (l2!=null){
            str2.insert(0,l2.val);
//            str2.append(l2.val);
            l2=l2.next;
        }
        long num=Long.parseLong(str1.toString())+Long.parseLong(str2.toString());
        String num1=String.valueOf(num);

        char n=num1.charAt(0);//char型输出的是ASCII码54
        //首节点
        ListNode node = new ListNode(num1.charAt(num1.length()-1)-'0');
        ListNode nextNode;
        nextNode=node;
        for (int i = num1.length()-2; i >= 0; i--) {
            ListNode newNode=new ListNode(num1.charAt(i)-'0');
            nextNode.next=newNode;
            nextNode=nextNode.next;
        }
        return node;
    }

2.2 考虑进位,一位一位加

题解链接

package Hot100;
import Hot100.addTwoNumbers02_1.ListNode;
/**
 * @author : lkw
 * @data : 2021/9/4 20:12
 * @description :有进位的
 **/
public class addTwoNumbers02_2 {
    public static ListNode addTwoNumbers(ListNode l1,ListNode l2) {
        //l1 = [2,4,3], l2 = [5,6]
        //3->4->2->null   +   6->5
        ListNode pre=new ListNode(0);//l3为起始指针的前一个
        ListNode nextNode=pre;
        int carry =0;//进位
        while (l1!=null || l2!=null){
            int x= l1!=null ? l1.val: 0;//位数不够补零进行计算
            int y= l2!=null ? l2.val: 0;
            int num = x+y+carry;
            int curNum=num%10;//当前位
            carry=num/10;//进位
            nextNode.next=new ListNode(curNum);
            nextNode=nextNode.next;

            if(l1!=null){
                l1=l1.next;
            }
            if(l2!=null){
                l2=l2.next;
            }
        }
//如果最后有进位,则在最前面添加1
        if (carry==1) {
            nextNode.next=new ListNode(carry);
        }

        return pre.next;


    }

}

9月5日

3. 无重复字符的最长子串

3.1 循环两次,记录从每个元素开始的最长子串长度

时间复杂度O(n^2)

package Hot100;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

/**
 * @author : lkw
 * @data : 2021/9/5 10:24
 * @description :3. 无重复字符的最长子串
 * 新建一个数组,放从这个词往后的不重复的长度,最后取出数组中最大的值
 *
 **/
public class lengthOfLongestSubstring3_01 {
    public static void main(String[] args) {
        String  s = "pwwkew";
        lengthOfLongestSubstring(s);
    }
    public static int lengthOfLongestSubstring(String s) {
        int max =0;
        int nums[]=new int[s.length()];
        if (s=="") return 0;
        if (s.length()==1) return 1;
        if (s.length()>1){
            for (int i = 0; i < s.length()-1; i++) {
                Set<String> set = new HashSet<>();
                set.add(s.substring(i,i+1));
                int j;
                for (j = i+1; j <s.length() ; j++) {
                    String m=s.substring(j,j+1);
                    if(!set.contains(m)){
                        set.add(m);
                    }else {break;}

                }
                    nums[i]=j-i;
                    max =max>j-i ? max :j-i;
            }
            nums[s.length()-1]=1;
        }
        return max;
    }

}

3.2 滑动窗口 

https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/hua-jie-suan-fa-3-wu-zhong-fu-zi-fu-de-zui-chang-z/

  1. start不动,end向后移动
  2. 当end遇到重复字符,start应该放在上一个重复字符的位置的后一位,同时记录最长的长度
  3. 怎样判断是否遇到重复字符,且怎么知道上一个重复字符的位置?--用哈希字典的key来判断是否重复,用value来记录该字符的下一个不重复的位置。

时间复杂度O(n)

package Hot100;

import java.util.HashMap;

/**
 * @author : lkw
 * @data : 2021/9/5 11:13
 * @description :可以不用循环两次,
 * map中存的是<值,值的位置>,如果有重复的值出现,重新定义窗口左侧,并且重写put新的位置
 **/
public class lengthOfLongestSubstring3_02 {
    public static void main(String[] args) {
        String s = "au";

        lengthOfLongestSubstring(s);
    }

    public static int lengthOfLongestSubstring(String s) {
        int max = 0;
        int left=0;
        char m;
        if (s == "") return 0;
        if (s.length() == 1) return 1;
        if (s.length() > 1) {
            HashMap<Character, Integer> map = new HashMap() ;
            for (int i = 0; i < s.length() ; i++) {
                m=s.charAt(i);
                if (map.containsKey(m)){
                    left=Math.max(left,map.get(m)+1);
                }
                map.put(m,i);
                max=Math.max(max,i-left+1);
            }

        }
        return max;
    }
}

4. 寻找两个正序数组的中位数

4.1 合并数组,再找到中位数

时间复杂度:O(m+n)

空间复杂度:O(m+n)

 左边界下标是:(n-1)/2

package Hot100;

/**
 * @author : lkw
 * @data : 2021/9/5 15:12
 * @description :4. 寻找两个正序数组的中位数
 * 先排序,找中位数
 **/
public class findMedianSortedArrays4_01 {
    public static void main(String[] args) {
        int[] nums1 = {1,3};
        int[] nums2 = {2};
        findMedianSortedArrays(nums1,nums2);
    }

    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int[] num3= new int[nums1.length+nums2.length];
        int i=0,j=0,n=0;
        while (i<nums1.length && j<nums2.length){
            if (nums1[i]<nums2[j]){
                num3[n++]=nums1[i++];
            }else {
                num3[n++]=nums2[j++];
            }
        }
        while (i<nums1.length){
            num3[n++]=nums1[i++];
        }
        while (j<nums2.length){
            num3[n++]=nums2[j++];
        }
//       0 1 2 3
        if ((n-1)%2==0){
            return num3[(n-1)/2];
        }else
            return (num3[(n-1)/2]+num3[(n-1)/2+1])/2.0;

    }
}

9.7日

5. 最长回文子串

中心扩展算法

题解链接:力扣

class Solution {
         //从中间向两边比较好判断
    public String longestPalindrome(String s) {
        int len=s.length();
        String s1 = "", s2="",res="";
        if (len<=1) return s;
        for (int i = 0; i < len; i++) {
            s1=Palindrome(s,i,i);
            s2=Palindrome(s,i,i+1);
            res=(res.length()>s2.length())?res:s2;
            res=(res.length()>s1.length())?res:s1;
        }
        return res;
    }

    private String Palindrome(String s, int left, int right) {
        //向两边延伸
        while (left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){
            left--;
            right++;
        }
        return s.substring(left+1,right);//substring是前闭后开区间
    }
}

注意: while ( s.charAt(start)==s.charAt(end) && start>=0 && end<s.length()){ }和

 while ( start>=0 && end<s.length() && s.charAt(start)==s.charAt(end) ){ }

前者会报错,因为start和end可能越界会报错,&&连接的判断,前面的不满足,后面的就不会执行。

9月11日10. 正则表达式匹配

10. 正则表达式匹配(难)

题解链接

本题未全部理解

 char[] cs = s.toCharArray();//将字符串转为字符串数组

package Hot100;

/**
 * @author : lkw
 * @data : 2021/9/11 14:27
 * @description :'.' 匹配任意单个字符    '*' 匹配零个或多个前面的那一个元素
 **/
public class isMatch10_01 {
    public static void main(String[] args) {
        String s = "aab";
        String p = "c*a*b";
        isMatch(s,p);
    }

    public static boolean isMatch(String s, String p) {
        char[] cs = s.toCharArray();
        char[] cp = p.toCharArray();

        // dp[i][j]:表示s的前i个字符,p的前j个字符是否能够匹配
        boolean[][] dp = new boolean[cs.length + 1][cp.length + 1];

        // 初期值
        // s为空,p为空,能匹配上
        dp[0][0] = true;
        // p为空,s不为空,必为false(boolean数组默认值为false,无需处理)

        // s为空,p不为空,由于*可以匹配0个字符,所以有可能为true
        //减2的原因:若出现*,则要裁掉2个
        // 例如:aa*  c*可以匹配空裁掉之后只剩下a,若裁后的不可以匹配空dp[0][j - 2],则dp[0][j]也不行行
        for (int j = 1; j <= cp.length; j++) {
            if (cp[j - 1] == '*') {//若出现*,则要裁掉2个
                dp[0][j] = dp[0][j - 2];//a*b*,若a*可以匹配空dp[0][j - 2],则b*也能匹配空dp[0][j]
            }
        }

        // 填格子
        for (int i = 1; i <= cs.length; i++) {
            for (int j = 1; j <= cp.length; j++) {
                // 文本串和模式串末位字符能匹配上
                if (cs[i - 1] == cp[j - 1] || cp[j - 1] == '.') {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (cp[j - 1] == '*') { // 模式串末位是*
                    // 模式串*的前一个字符能够跟文本串的末位匹配上
                    if (cs[i - 1] == cp[j - 2] || cp[j - 2] == '.') {
                        dp[i][j] = dp[i][j - 2]      // *匹配0次的情况
                                || dp[i - 1][j];     // *匹配1次或多次的情况
                    } else { // 模式串*的前一个字符不能够跟文本串的末位匹配
                        dp[i][j] = dp[i][j - 2];     // *只能匹配0次
                    }
                }
            }
        }
        return dp[cs.length][cp.length];
    }
}

因为这边定义的dp[i][j]是表示s的前i个字符和p的前j个字符能否匹配。s一共有s.length个字符,p一共有p.length个字符。最终答案就是dp[s.length][p.length],而数组是从下标0开始的,所以dp数组的长度是s.length+1和p.length+1。

11. 盛最多水的容器

题解链接

暴力超时

    public static int maxArea(int[] height) {
//将短板忘内挪
        int i = 0, j = height.length - 1, max = 0;
        while (i<j){
            max = height[i]<height[j]?
                    Math.max(max,(j-i)*height[i++]):
                    Math.max(max,(j-i)*height[j--]);
            }
        return max;
        }

15. 三数之和

题解链接

 public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        int len = nums.length;
        if (nums == null || len < 3) {
            return ans;
        }
        Arrays.sort(nums);
        for (int i = 0; i < len; i++) {
            if (nums[i] > 0) break;
            if (i > 0 && nums[i] == nums[i - 1]) continue;//i>0只是为了让i-1不越界
            int L = i + 1;
            int R = len - 1;
            while (L < R) {
                int sum = nums[i] + nums[L] + nums[R];
                if (sum == 0) {
                    ans.add(Arrays.asList(nums[i], nums[L], nums[R]));
                    while (L < R && nums[L] == nums[L + 1]) {
                        L++;
                    }
                    while (L < R && nums[R] == nums[R - 1]) {
                        R--;
                    }
                    L++;
                    R--;
                } else if (sum < 0) L++;
                else if (sum > 0) R--;

            }
        }
        return ans;
    }

2021年9月20

17. 电话号码的字母组合

题解

回溯 

package Hot100;

import java.security.Key;
import java.util.*;

/**
 * @author : lkw
 * @data : 2021/9/20 10:30
 * @description :map.put("2", Arrays.asList("a", "b", "c"));//Map<String,List<String>>的写法
 **/
public class letterCombinations17_01 {
    private static final String[] KET ={"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    public static void main(String[] args) {
        final String[] KET;
        String digits = "23";
        letterCombinations(digits);
    }
    public static List<String> letterCombinations(String digits) {
        List<String> list=new ArrayList<>();
        if (digits.length()==0 || digits==null) return list;
        Combinations(new StringBuffer(),list,digits);
        return list;
    }

    public static void Combinations(StringBuffer pre, List<String> list,String digits){
        if (pre.length()==digits.length()){
            list.add(pre.toString());
            return ;
        }
        int curDigits=digits.charAt(pre.length())-'0';
        String str= KET[curDigits];
        for (char c: str.toCharArray()) {
            pre.append(c);
            Combinations(pre,list,digits);
            pre.deleteCharAt(pre.length()-1);
        }

    }

}

19. 删除链表的倒数第 N 个结点

题解链接

方法一:计算长度

package Hot100;

/**
 * @author : lkw
 * @data : 2021/9/20 20:41
 * @description :
 **/
public class removeNthFromEnd19_01 {
    public static class ListNode {
        int val;
        ListNode next;
        ListNode() {
        }
        ListNode(int val) {
            this.val = val;
        }
        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }

    public static void main(String[] args) {
        ListNode head = new ListNode(1);
        ListNode node = head;
        int n = 1;
        for (int i = 2; i <= n; i++) {
            node.next = new ListNode(i);
            node = node.next;
        }
        removeNthFromEnd(head, 1);
    }

    public static ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode node = head;
        int len = 0;
        while (node != null) {
            len++;//计算长度
            node = node.next;
        }
        node = head;
        if (len == n){
            return node.next;//如果删除的是头
        }
        node = head;
        for (int i = 1; i < len - n; i++) {
            node = node.next;
        }
        node.next = node.next.next;//删除的是中间元素

        return head;

    }

}

方法二:优化方法一(哑节点) 

在对链表进行操作时,一种常用的技巧是添加一个哑节点(dummy node),它的next 指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。

 public static ListNode removeNthFromEnd(ListNode head, int n) {
        //哑节点,不需要考虑头
        ListNode dummy = new ListNode(0, head);
        ListNode cur = dummy;
        int len = 0;
        while (head != null) {
            len++;//计算长度
            head = head.next;
        }
        for (int i = 1; i < len-n+1; i++) {
           cur=cur.next;
        }
        cur.next=cur.next.next;
        return dummy.next;

    }

方法三:双指针

public static ListNode removeNthFromEnd(ListNode head, int n) {
        //哑节点,不需要考虑头
        ListNode dummy = new ListNode(0, head);
        ListNode first =head;//first比second前挪nge
        ListNode second = dummy;
        //挪到要删节点的前驱
        for (int i = 0; i < n; i++) {
            first=first.next;
        }
        while (first!=null) {
            first=first.next;
            second=second.next;
        }
        second.next=second.next.next;
        return dummy.next;

    }

9月21日

20. 有效的括号

//pop() 方法弹出栈顶元素, 调用后, 栈顶元素从栈中被永久性地删除。
//peek() 方法则只返回栈顶元素, 而不删除它。
 public static boolean isValid(String s) {
        Stack<Character> stack=new Stack<>();
        stack.push('?');
        HashMap<Character,Character> map =new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
        map.put('?','?');
        for (Character c:s.toCharArray()) {
            if (map.containsKey(c)){
                stack.push(c);//如果是左括号则放入
            }else if(c!=map.get(stack.pop())){
                return false;
            }
        }
        return stack.size()==1;
    }

9月22

23. 合并K个升序链表

package Hot100;

/**
 * @author : lkw
 * @data : 2021/10/2 9:48
 * @description :每次都得比较k个中最小的
 **/
public class mergeKLists23_01 {
    public static class ListNode {
        int val;
        ListNode next;

        ListNode() {
        }

        ListNode(int val) {
            this.val = val;
        }

        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }


    public ListNode mergeKLists(ListNode[] lists) {
        ListNode ans = null;
        int nums=lists.length;
        for (int i = 0; i < nums; i++) {
            ans= mergetwoLists(ans,lists[i]);
        }

        return ans;
    }
    public ListNode mergetwoLists(ListNode l1,ListNode l2) {
        if (l1==null || l2==null){
            return l1!=null?l1:l2;
        }
        ListNode dummy = new ListNode(0);
        ListNode newnode = dummy;
        //怎么三者比出最小的?
    while (l1!=null && l2!=null){
            if (l1.val<=l2.val){
                newnode.next=l1;
                l1=l1.next;
            }else {
                newnode.next=l2;
                l2=l2.next;
            }
       newnode=newnode.next;
    }
    newnode.next=l1!=null?l1:l2;
    return dummy.next;
    }
}

21. 合并两个有序链表

package Hot100;

/**
 * @author : lkw
 * @data : 2021/9/22 20:11
 * @description :不需要节点再记录l1和l2的位置
 **/
public class mergeTwoLists21_01 {
    public static class ListNode {
     int val;
     ListNode next;
     ListNode() {}
     ListNode(int val) { this.val = val; }
     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 }

    public static void main(String[] args) {
//        l1 = [1,2,4], l2 = [1,3,4];
//        ListNode l1= new ListNode(1);
//        l1.next=new ListNode(2);
//        l1.next.next=new ListNode(4);
//
//        ListNode l2= new ListNode(1);
//        l2.next=new ListNode(3);
//        l2.next.next=new ListNode(4);
        ListNode l1= null;
        ListNode l2= null;
        mergeTwoLists(l1,l2);
    }

    public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //哑节点

        ListNode ans = new ListNode(-999);
        ListNode cur3 = ans;
        while (l1!=null && l2!=null){
            if (l1.val<=l2.val){
                cur3.next= l1;
                l1=l1.next;
            }else {
                cur3.next= l2;
                l2=l2.next;
            }
            cur3=cur3.next;
        }
//        if (l1!=null){
//            cur3.next=l1;
//        }else cur3.next=l2;
        cur3.next= l1!=null?l1:l2;

        return ans.next;
    }

}

9月25日

22. 括号生成

题解

回溯法:

如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。

class Solution {
   public static List<String> generateParenthesis(int n) {
        List<String> ans = new ArrayList<>();
        backing(ans,0,0,new StringBuffer(),n);
        return ans;
    }

    public static void backing( List<String> ans,int left,int right,StringBuffer str,int n){
        if (str.length()==2*n){
            ans.add(str.toString());
            return;
        }
        //如果左括号的个数少于n,则可以加入左括号
        if (left<n){
            str.append("(");
            backing(ans,left+1,right,str,n);
            str.deleteCharAt(str.length()-1);
        }
        //右括号的个数少于左括号,可以加入左括号
        if (right<left){
            str.append(")");
            backing(ans,left,right+1,str,n);
            str.deleteCharAt(str.length()-1);
        }
    }
}

10.2日

23. 合并K个升序链表

方法一:顺序合并

package Hot100;

/**
 * @author : lkw
 * @data : 2021/10/2 9:48
 * @description :
 **/
public class mergeKLists23_01 {
    public static class ListNode {
        int val;
        ListNode next;

        ListNode() {
        }

        ListNode(int val) {
            this.val = val;
        }

        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }


    public ListNode mergeKLists(ListNode[] lists) {
        ListNode ans = null;
        int nums=lists.length;
        for (int i = 0; i < nums; i++) {
            ans= mergetwoLists(ans,lists[i]);
        }

        return ans;
    }
    public ListNode mergetwoLists(ListNode l1,ListNode l2) {
        if (l1==null || l2==null){
            return l1!=null?l1:l2;
        }
        ListNode dummy = new ListNode(0);
        ListNode newnode = dummy;
        //怎么三者比出最小的?
    while (l1!=null && l2!=null){
            if (l1.val<=l2.val){
                newnode.next=l1;
                l1=l1.next;
            }else {
                newnode.next=l2;
                l2=l2.next;
            }
       newnode=newnode.next;
    }
    newnode.next=l1!=null?l1:l2;
    return dummy.next;
    }
}

方法二:分治合并

public ListNode mergeKLists(ListNode[] lists) {
        return merge(lists,0, lists.length-1);
    }
    public ListNode merge(ListNode[] lists,int left,int right){
        if (left==right) return lists[left];
        if (left>right) return null;
        int mid = left+(right-left)/2;//防止溢出
        return mergetwoLists(merge(lists,left,mid),merge(lists,mid+1,right));

    }
    
    public ListNode mergetwoLists(ListNode l1, ListNode l2) {
        if (l1==null || l2==null){
            return l1!=null?l1:l2;
        }
        ListNode dummy = new ListNode(0);
        ListNode newnode = dummy;
        //怎么三者比出最小的?
        while (l1!=null && l2!=null){
            if (l1.val<=l2.val){
                newnode.next=l1;
                l1=l1.next;
            }else {
                newnode.next=l2;
                l2=l2.next;
            }
            newnode=newnode.next;
        }
        newnode.next=l1!=null?l1:l2;
        return dummy.next;
    }

31. 下一个排列

 思路

 1.从后往前找到第一次出现升序的地方(位置是i和i+1)

2.找到i之后比i大的最小值k

3.调换i和k位置上的值

4.将i之后的数字升序排序

代码看:https://leetcode-cn.com/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-leetcode-solution/

题解思路看:https://leetcode-cn.com/problems/next-permutation/solution/xia-yi-ge-pai-lie-suan-fa-xiang-jie-si-lu-tui-dao-/

package Hot100;

/**
 * @author : lkw
 * @data : 2021/10/2 9:57
 * @description :
 **/
public class nextPermutation31_01 {
    public static void main(String[] args) {
        int[] nums={5,4,7,5,3,2};
        nextPermutation(nums);
    }


    public static void nextPermutation(int[] nums) {
        if (nums.length<2) return;//1,3
        int i;
        //从后往前找到第一次出现升序 nums[i]<nums[i+1]的地方(因为是第一次出现的,因此i+1之后的都是降序的)
        for (i = nums.length-2; i >=0 && nums[i]>=nums[i+1]; i--);
        int k;
        //如果序列本来是倒序,例如:3,2,1 则i=-1
        if (i>=0){
            //k是找到i后面比i大的最小值
            for (k = nums.length-1; k >i && nums[k]<=nums[i]; k--);
            swap(nums,i,k);
        }
        reverse(nums,i+1);
    }
    public static void swap(int[] nums,int index1,int index2){
        int temp=nums[index1];
        nums[index1]=nums[index2];
        nums[index2]=temp;
    }
//重排从start开始的元素
    public static void reverse(int[] nums,int start){
        int end=nums.length-1;
        while (start<end){
            swap(nums,start++,end--);
        }
    }
}

10月3日

32. 最长有效括号

题解

package Hot100;

import java.util.Stack;

/**
 * @author : lkw
 * @data : 2021/10/3 10:22
 * @description :
 **/
public class longestValidParentheses32_01 {
    public static void main(String[] args) {
        String s = "()(()";
        longestValidParentheses(s);
    }
    public static int longestValidParentheses(String s) {
        char[] str=s.toCharArray();
        int max=0;
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        for (int i = 0; i < s.length(); i++) {

            if (str[i]=='('){
                stack.push(i);
            }else {
                //遇到右括号先出栈
                stack.pop();
                if (stack.isEmpty()){
                    stack.push(i);
                }else
                    max=Math.max(i-stack.peek(),max);
            }
            }
        return max;

    }
}

10月4日 

33. 搜索旋转排序数组

二分法:题解

将数组从中间分开成左右两部分的时候,一定有一部分的数组是有序的。先判断目标值是否在有序部分(因为有序的好判断,通过左右边界大小直接判断),如果不在有序这边,那一定在另一部分。

先判断目标值是否在有序的这边,如果不在搜索另一部分。

那如何判断是否为有序数组:

如果nums[left]<=nums[mid]则是有界的,判断其左右边界与target大小关系。

package Hot100;

/**
 * @author : lkw
 * @data : 2021/10/4 15:10
 * @description :旋转排序数组,实际是想告诉我们两个升序数组
 **/
public class search33_01 {
    public static void main(String[] args) {
        int[] nums = {3,1};
        int target = 1;
        search(nums,target);
    }
    public static int search(int[] nums, int target) {
        int len=nums.length;
        if (len==0) return -1;
        if (len==1) return nums[0]==target?0:-1;
        int left=0;
        int right= len-1;
        while(left<=right){
            int mid = (left+right)/2;
            if (nums[mid]==target) return mid;
            //左侧有序
            if (nums[left]<=nums[mid]){//等于的情况就是mid就是left
                if (nums[left]<=target && nums[mid]>target ){
                    right=mid-1;
                }else
                    left=mid+1;
            }else{//右侧有序
                if (nums[right]>=target && nums[mid]<target ){
                    left=mid+1;
                }else right=mid-1;
            }
        }
        return -1;
    }
}

10月7日

34. 在排序数组中查找元素的第一个和最后一个位置

1.二分法: 

题解链接

1.1容易理解,推荐

思路:

1.二分查找找到nums[mid]==target.

2.再找出mid左侧和右侧,是否还存在target.

    public int[] searchRange(int[] nums, int target) {
        //1.二分查找找到nums[mid]==target 2.再找出mid左侧和右侧,是否还存在target
        if (nums.length==0) return new int[]{-1,-1};
        int mid=0;
        int left=0;
        int right= nums.length-1;
        while (left<=right){
            mid=left+(right-left)/2;
            if (nums[mid]==target) break;
            else if (nums[mid]<target){
                left=mid+1;
            }else right=mid-1;
        }
        if (nums[mid]==target) {
            left=right=mid;
        }else return new int[]{-1,-1};

        while (left>=1 && nums[left]==nums[left-1]){
            left--;
        }
        while (right< nums.length-1 && nums[right]==nums[right+1]){
            right++;
        }
        return new int[]{left,right};
    }

1.2 不易理解 

 public static int[] searchRange(int[] nums, int target) {

        int leftNum=binary(nums,target,true);
        int rightNum=binary(nums,target,false)-1;//右侧找到的是大于target的第一个元素
        if (leftNum<=rightNum && leftNum>=0 && rightNum<=nums.length-1 &&nums[rightNum]==target && nums[leftNum]==target ){
            return new int[]{leftNum,rightNum};
        }
        return new int[]{-1,-1};
    }

    public static int binary(int[] nums, int target,boolean le){
        //le为true代表找到是最左侧元素
        int left=0;
        int right= nums.length-1;
        int ans= nums.length;//若未找到目标元素,返回ans,在上级判断是会越界,则返回{-1,-1}
        while(left<=right){
            //nums[mid]<target时,查找leftNum和rightNum都找左侧区间,
            //在nums[mid]=target情况下,查找leftNum时查找左侧区间,查找rightNum是查找右侧区间,
            //因此使用le标记查找的是leftNum还是rightNum
            int mid= left+(right-left)/2;
            if (nums[mid]>target || (le==true && nums[mid]==target) ){
                //查找[left,mid-1]
                right=mid-1;
                ans=mid;

            }else{
                left=mid+1;
            }

        }
        return ans;
    }

 2.暴力解法:

找到第一个与target相等的值为左边届,然后记录其后面,与target相等的个数,来定位右边界。

 public static int[] searchRange(int[] nums, int target) {
        boolean flag=false;
        int count=0;
        int left=-1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]>target){
                break;
        }
            if (nums[i]==target){
                if (!flag){
                    left=i;
                    flag=true;
                }else
                    count++;
            }
        }
            return new int[]{left, left+count};
        }

39. 组合总和

res.add(ans);//存的是实时的ans,ans变则res中的值也变。引用的是同一个地址

因此要使用res.add(new ArrayList<>(ans));

  

package Hot100;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @author : lkw
 * @data : 2021/10/7 15:52
 * @description :
 **/
public class combinationSum39_01 {
    public static void main(String[] args) {
        int[] candidates = {2,3,6,7};
        int target = 7;
        combinationSum(candidates,target);
    }

    public static List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> lists =new ArrayList<>();
        List<Integer> ans=new ArrayList<>();
backing(candidates,lists,ans,0,target);
        return lists;
    }

    public static void backing(int[] candidates, List<List<Integer>> lists,
                               List<Integer> ans,int start,int target){
        if (target ==0){
            lists.add(new ArrayList<>(ans));
            return;
        }
        for (int i = start; i < candidates.length ; i++) {
            if (candidates[i]<=target){
                ans.add(candidates[i]);
                backing(candidates,lists,ans,i,target-candidates[i]);
                ans.remove(ans.size()-1);
            }
        }

    }
}

10月8日

42. 接雨水

题解

1.动态编程

从左往右找最大的left,从右往左找最大的right
 public int trap(int[] height) {
        int size=height.length;
        if(size==0 || height==null){
            return 0;
        }
        int ans=0;
        int[] left_max=new int[size];
        int[] right_max=new int[size];
        left_max[0]=height[0];
        for (int i = 1; i < size; i++) {
            left_max[i]=Math.max(left_max[i-1],height[i]);
        }
        right_max[size-1]=height[size-1];
        for (int i = size-2; i >=0 ; i--) {
            right_max[i]=Math.max(right_max[i+1],height[i]);
        }
        for (int i = 0; i < size; i++) {
            ans+=Math.min(right_max[i],left_max[i])-height[i];
        }
        return ans;
    }
    public int trap(int[] height) {
        int len=height.length;
//从左往右找最大的left,从右往左找最大的right
        int[] left=new int[len];
        int[] right=new int[len];
        right[len-1]=height[len-1];
        left[0]=height[0];
        int sum=0;
        for (int i = 1; i < len; i++) {
            int j=len-1-i;
            left[i]=Math.max(left[i-1],height[i]);
            right[j]=Math.max(right[j+1],height[j]);
        }
        for (int i = 0; i < len; i++) {
            sum+=Math.min(left[i],right[i])-height[i];
        }

        return sum;
    }

46. 全排列

题解

 public static List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> lists =new ArrayList<>();
        List<Integer> ans= new ArrayList<>();
        boolean[] visited=new boolean[nums.length];
            backing(nums,lists,ans,visited);
            return lists;

    }

    private static void backing(int[] nums,List<List<Integer>> lists,
                                List<Integer> ans,boolean[] visited) {

        if (ans.size()== nums.length){
            lists.add(new ArrayList<>(ans));
        }
        for (int i = 0; i < nums.length ; i++) {
            if (visited[i]){
                continue;
            }
            visited[i]=true;
            ans.add(nums[i]);
            backing(nums,lists,ans,visited);
            ans.remove(ans.size()-1);//移除索引位置,从0开始
            visited[i]=false;
        }
    }

48. 旋转图像

题解

1.方法一:使用辅助数组

       public void rotate(int[][] matrix) {
        int len = matrix.length;
        int[][] max_new =  new int[len][len];
        for (int i = len-1; i >=0; i--) {
            for (int j = 0; j < len; j++) {
                max_new[j][len-i-1]=matrix[i][j];
            }
        }

        for (int i = 0; i < len; i++) {
            for (int j = 0; j < len; j++) {
                matrix[i][j]=max_new[i][j];
            }
        }

    }

2.方法二:旋转

public void rotate(int[][] matrix) {
        int len =matrix.length;
        int temp;
        //上下翻转
        for (int i = 0; i < len/2; i++) {
            for (int j = 0; j < len; j++) {
                temp=matrix[len-i-1][j];
                matrix[len-i-1][j]=matrix[i][j];
                matrix[i][j]=temp;
            }
        }
        //对称轴翻转
        for (int i = 0; i < len; i++) {
            for (int j = 0; j < i; j++) {
                temp=matrix[j][i];
                matrix[j][i]=matrix[i][j];
                matrix[i][j]=temp;
            }
        }
    }

10月11日

49. 字母异位词分组

//String key=strChar.toString();//通过不了,引用的是地址
String key=new String(strChar);
package Hot100;

import java.util.*;

public class groupAnagrams_49 {
    public static void main(String[] args) {
        String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"};
        groupAnagrams(strs);
    }
    public static List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> map=new HashMap<String,List<String>>();
        for (String str:strs) {
            char array[]=str.toCharArray();
            Arrays.sort(array);
            String key = new String(array);
            String key1 =array.toString();//这样转字符串不对,因为数组是对象,没有重写toString方法,因此得到的是类签名

            List<String> list=map.getOrDefault(key,new ArrayList<>());
            list.add(str);
            map.put(key,list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

注意:

1.getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。

 2. new String() 和 toString()的区别

Java对象都继承于Object,Object中提供了toString方法,用于简单返回该类的类签名。在Java中,数组也可以看作是一种对象,显然byte[]也是一种继承与Object的对象,并且它没有重写Object的toString方法,因此使用byte[]的toString返回的字符串,仅仅是byte[]的类签名,而不是对应的值。

改为使用new String()构造方法,将byte[]转换为字符串,得到的就会是一个根据字节数组内容构造的字符串。

总结:new String()一般使用字符转码的时候,例如byte[]数组的时候。
          toString()将对象打印的时候使用。

53. 最大子序和

  public static int maxSubArray(int[] nums) {
        int cur=nums[0];
        int max=nums[0];
        for (int i = 1; i < nums.length; i++) {
            if(cur>0){
                cur=cur+nums[i];
            }else {
                //如果当前值前面的和为负数,则舍弃前面的值,
                cur=nums[i];
            }
            max=Math.max(cur,max);
        }
            return max;
    }

10月12日

  55. 跳跃游戏

题解

 public static boolean canJump(int[] nums) {
        int n= nums.length;
        if (n==1) { return nums.length>=0;}
        int rightmost=nums[0];//若nums={0},则通过不了,需要加if判断n==1
        //int rightmost=0;//for循环从0开始
        for (int i = 1; i < n; i++) {
            if (i<=rightmost){
                rightmost=Math.max(rightmost,i+nums[i]);
                    if (rightmost>=n-1){
                        return true;
                    }
            }
        }
        return false;
    }
}


//    public boolean canJump(int[] nums) {
//        int n = nums.length;
//        int rightmost = 0;
//        for (int i = 0; i < n; ++i) {
//            if (i <= rightmost) {
//                rightmost = Math.max(rightmost, i + nums[i]);
//                if (rightmost >= n - 1) {
//                    return true;
//                }
//            }
//        }
//        return false;
//    }

56. 合并区间

题解

思路:
1.按首元素排序
2.取当前遍历元素的左右边界,若列表最后一个元素右边界<左边界,则不能合并,直接插入。
3.若能合并,只修改列表最后一个元素右边界即可。merges.get(merges.size()-1)[1]=right;//取的是地址,因此直接赋值就行
public static int[][] merge(int[][] intervals) {
         List<int[]> merges=new ArrayList<>();
        if (intervals.length==0){
            return new int[0][2];
        }
         //按首元素升序排序
        Arrays.sort(intervals, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0]-o2[0];//o1-o2为升序,o2-o1为降序
            }
        });
         //取当前遍历元素的左右边界,若列表最后一个元素右边界<左边界,则不能合并,直接插入
        //合并,只修改列表最后一个元素右边界即可
        for (int i = 0; i < intervals.length; i++) {
            int left=intervals[i][0];
            int right=intervals[i][1];
            if (merges.size()==0|| merges.get(merges.size()-1)[1]<left){
                merges.add(new int[]{left,right});
            }else{
                merges.get(merges.size()-1)[1]=Math.max(merges.get(merges.size()-1)[1],right);//取的是地址,因此直接赋值就行
            }
        }
        return merges.toArray(new int[merges.size()][]);
    }

 注意:

      //按首元素升序排序
        Arrays.sort(intervals, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0]-o2[0];//o1-o2为升序,o2-o1为降序
            }
        });

return merges.toArray(new int[merges.size()][]);//括号是merges转成的int数组大小 

 

10月13日

62. 不同路径

题解

1.动态规划

 public int uniquePaths(int m, int n) {
        int[][] de=new int[m][n];
        for (int i = 0; i < m; i++) {
            de[i][0]=1;
        }
        for (int i = 0; i < n; i++) {
            de[0][i]=1;
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j <n ; j++) {
                de[i][j]=de[i-1][j]+de[i][j-1];
            }
        }
        return de[m-1][n-1];
    }

2.数学

    public int uniquePaths(int m, int n) {
        double ans=1;
        for (int x = 1,y=n; x < m; x++,y++) {
            ans=ans*y/x;
        }
        return (int)ans;
    }

64. 最小路径和

题解

   public int minPathSum(int[][] grid) {
        if (grid==null||grid.length==0){
            return 0;
        }
        int m=grid.length;
        int n=grid[0].length;
        int[][] de =new int[m][n];
        de[0][0]=grid[0][0];
        for (int i = 1; i < m; i++) {
            de[i][0]=de[i-1][0]+grid[i][0];
        }
        for (int i = 1; i <n; i++) {
            de[0][i]=de[0][i-1]+grid[0][i];
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                de[i][j]=Math.min(de[i-1][j],de[i][j-1])+grid[i][j];
            }
        }
        return de[m-1][n-1];
    }

10月14日

70. 爬楼梯

1.动态规划:动态数组

题解

 public static int climbStairs(int n){
        int a=1;int b=1;int c=1;
        for (int i = 1; i < n; i++) {
            a=b;
            b=c;
            c=a+b;
        }
        return c;
    }

75. 颜色分类

方法一:排序

       //冒泡排序
    public static void sortColors(int[] nums) {
        int len= nums.length;
        for (int i = len-1; i >=0; i--) {
            for (int j = 0; j < i; j++) {
                if (nums[j]>nums[j+1]){
                    int temp=nums[j+1];
                    nums[j+1]=nums[j];
                    nums[j]=temp;
                }
            }
        }
    }

方法二: 

此代码中: 0 的优先级> 1 的优先级 >2 的优先级

即时2先被填入,但是若最后值不为2,会被覆盖掉。

 public static void sortColors(int[] nums) {
        //zero表示数字0的右侧边界,one、two分别表示1 和 2 的右侧边界
        int zero = 0;int one = 0;int two = 0;
        int length = nums.length;
        for(int i = 0; i < length; i++) {
            //记录下待处理的值
            int temp = nums[i];
            //不管怎样,我先给你填个2
            nums[two++] = 2;
            // <=1的话 1的右侧边界one要向右挪一格
            if(temp <= 1){
                nums[one++] = 1;    
            }
            //为0的话 0的右侧边界zero要向右挪一格
            if(temp == 0) {
                nums[zero++] = 0;  
            }
        }
    }

10月16日

72. 编辑距离

题解

 

 

dp数组类似这样:

 

public int minDistance(String word1, String word2) {
        int m=word1.length();
        int n=word2.length();
        //一个字符串为空
        if (m*n==0) return m+n;
        int dp[][]=new int[m+1][n+1];
        for (int i = 0; i < m+1; i++) {
            dp[i][0]=i;
        }
        for (int i = 0; i < n+1; i++) {
            dp[0][i]=i;
        }
        for (int i = 1; i < m+1; i++) {
            for (int j = 1; j < n+1; j++) {
                int delete=dp[i-1][j]+1;
                int insert=dp[i][j-1]+1;
                int alert=dp[i-1][j-1];
                //修改后前一个字符不相等,编辑距离加一
                if (word1.charAt(i-1)!=word2.charAt(j-1)){
                    alert++;
                }
                dp[i][j] =Math.min(delete,Math.min(insert,alert));
            }

        }
            return dp[m][n];
    }

10月19日

94. 二叉树的中序遍历

1.递归

public List<Integer> inorderTraversal(TreeNode root) {
      List<Integer> res=new ArrayList<>();
      inord(root,res);
      return res;

    }
    public void inord(TreeNode root,List<Integer> res){
            if (root==null){
                return;
            }
            inord(root.left,res);
            res.add(root.val);
            inord(root.right,res);
    }

2.迭代

Java中LinkedList add 是加在list尾部.

LinkedList push 施加在list头部.

  res.add(root.val);的位置决定前中后序遍历

public List<Integer> inorderTraversal(TreeNode root) {
        //!deque.isEmpty
        List<Integer> res=new ArrayList<>();
        Stack<TreeNode> deque=new Stack<>();
        while(root!=null || !deque.isEmpty()){
//判断栈是否为空用:deque.isEmpty()
            while (root!=null){
                deque.push(root);//deque.push(root);
                root=root.left;
            }
            root=deque.pop();
            res.add(root.val);
            root=root.right;
        }
            return res;
    }

3.颜色标记法

题解

  class ClassNode{
    TreeNode node;
    boolean flag;
    ClassNode(TreeNode node,boolean flag){
        this.flag=flag;
        this.node=node;
    }
}
    public List<Integer> inorderTraversal(TreeNode root) {
      List<Integer> res=new ArrayList<>();
      Stack<ClassNode> stack =new Stack<>();
      if (root==null) return new ArrayList<>();
      stack.push(new ClassNode(root,false));
      while (!stack.isEmpty()){
          ClassNode cn=stack.pop();
          if (cn.flag==false){
              if (cn.node.right!=null) stack.push(new ClassNode(cn.node.right,false));
              stack.push(new ClassNode(cn.node, true));
              if (cn.node.left!=null)stack.push(new ClassNode(cn.node.left,false));
          }else {
              res.add(cn.node.val);
          }
      }
      return res;
    }

10月20日

只要求形状不同,不要求节点的值。

96. 不同的二叉搜索树

题解

1.动态规划(自底向上,并且前面计算的G[i]后面会用到)

G[n]代表n个节点时的排列组合数。(中间为一个节点j,左边j-1个节点,右边i-j个)

 public static int numTrees(int n) {
        int G[]=new int[n+1];
        G[0]=1;G[1]=1;
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <=i; j++) {
                G[i]+=G[j-1]*G[i-j];
            }
        }
        return G[n];
    }

10月21

98. 验证二叉搜索树

1.中序遍历是顺序的

//中序遍历是顺序的
public boolean isValidBST(TreeNode root) {
             boolean flag=true;
            List<Integer> list = new ArrayList<>();
            inord(root,list);
            for (int i = 0; i < list.size()-1; i++) {
                for (int j = i+1; j < list.size(); j++) {
                    if (list.get(i)>=list.get(j)){
                        flag=false;
                        break;
                    }
                }
            }
            return flag;
        }
        public void inord(TreeNode root,List<Integer> list){
            if(root==null){return;}
            inord(root.left,list);
            list.add(root.val);
            inord(root.right,list);
        }

2.判断当前节点和中序遍历的前一个节点大小

class Solution {
      long pre = Long.MIN_VALUE;
   public  boolean isValidBST(TreeNode root) {
        if (root==null) return true;
        if (!isValidBST(root.left)){
            return false;
        }
        if (root.val<=pre){
            return false;
        }
        pre=root.val;
        return isValidBST(root.right);
}
}

101. 对称二叉树

1.递归

    public boolean isSymmetric(TreeNode root) {
        return check(root,root);
    }

    public boolean check(TreeNode left,TreeNode right){
        if (left==null && right==null)return true;
        if (left==null || right==null) return false;
        return left.val==right.val && check(left.right,right.left) && check(left.left,right.right);

    }

2.迭代

Queue 中 add() 和 offer()都是用来向队列添加一个元素。
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。

Queue使用时要尽量避免Collection的add()和remove()方法,add()和remove()方法在失败的时候会抛出异常。

要使用offer()来加入元素,使用poll()来获取并移出元素。

它们的优点是通过返回值可以判断成功与否, 如果要使用前端而不移出该元素,使用element()或者peek()方法。

 public boolean isSymmetric(TreeNode root) {
        return check(root,root);
    }

    public boolean check(TreeNode left, TreeNode right){
        Queue<TreeNode> que = new LinkedList<>();
        que.offer(right);
        que.offer(left);
        while(!que.isEmpty()){
            right=que.poll();
            left=que.poll();
            if (right==null && left==null){
                continue;
            }
            if (right==null || left==null || right.val!= left.val){
                return false;
            }
            que.offer(right.right);
            que.offer(left.left);
            que.offer(right.left);
            que.offer(left.right);
        }
return true;



    }

10月22

102. 二叉树的层序遍历

题解

层次遍历可由宽度优先遍历bfs而得。

宽度优先遍历bfs如下:

void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); 
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}

本题题解: 

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
         Queue<TreeNode> queue =new ArrayDeque<>();
         if (root!=null) queue.offer(root);
         while(!queue.isEmpty()){
             ArrayList<Integer> leval = new ArrayList<>();
             int n=queue.size();
             for (int i = 0; i < n; i++) {
                 TreeNode node =queue.poll();
                 leval.add(node.val);
                 if (node.left!=null) {
                     queue.offer(node.left);
                 }

                 if (node.right!=null) {
                     queue.offer(node.right);//add会抛异常,offer返回false
                 }
             }
        list.add(leval);
         }
        return list;
    }

补充

deque和queue的区别:

 从上图看出,Queue以及Deque都是继承于Collection,Deque是Queue的子接口。

在java中,Queue被定义成单端队列使用,Deque被定义成双端队列使用
而由于双端队列的定义,Deque可以作为栈或者队列使用,而Queue只能作为队列或者依赖于子类的实现作为堆使用

ArrayDeque通常作为栈或队列使用,但是栈的效率不如LinkedList高。
LinkedList通常作为或队列使用,但是队列的效率不如ArrayQueue高。

ArrayDeque.add()方法用于在双端队列的末尾添加给定元素。

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

104. 二叉树的最大深度

题解

1.dfs深度优先遍历

//dfs深度优先遍历  
 public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        } else {
            int leftHeight = maxDepth(root.left);
            int rightHeight = maxDepth(root.right);
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }

2. 层次遍历

//输出上题层次遍历的size即可
public int maxDepth(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        Queue<TreeNode> queue =new ArrayDeque<>();
        if (root==null) return 0;
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> level = new ArrayList<>();
            int n=queue.size();
            for (int i = 0; i < n; i++) {
                TreeNode node = queue.poll();
                level.add(node.val);
                if (node.left!=null) {
                    queue.add(node.left);
                }
                if (node.right!=null) {
                    queue.add(node.right);
                }
            }
            list.add(level);
        }
        return list.size();
    }

 3.根据bfs不存储最终结果,只输出高度

 public int maxDepth(TreeNode root) {
        int hight =0;
        Queue<TreeNode> queue =new ArrayDeque<>();
        if (root==null) return 0;
        queue.add(root);
        while(!queue.isEmpty()){
            int n =queue.size();
            while(n>0){
                TreeNode node = queue.poll();
                if (node.left!=null) {
                    queue.add(node.left);
                }
                if (node.right!=null) {
                    queue.add(node.right);
                }
                n--;
            }
            hight++;
        }
        return hight;
    }

10月24日

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

题解

HashMap<Integer, Integer> map = new HashMap<>();

    public TreeNode buildTree(int[] preorder, int[] inorder) {

        int n = preorder.length;
        //为了定位root所在位置
        for (int i = 0; i < n; i++) {
            map.put(inorder[i], i);
        }
        return mybuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }

    public TreeNode mybuildTree(int[] preorder, int[] inorder, int pre_left, int pre_right, int in_left, int in_right) {
        if (pre_left > pre_right) {
            return null;
        }
        //1.找到root后,在inorder中找到索引,得到左子树长度len=(中序root定位序号-left边界) 右子树preorder[root定位+1,root+len+1],就可以分开左右子树
        //前序的第一个就是根节点
        int pre_root = pre_left;
        int in_root = map.get(preorder[pre_root]);

        TreeNode root = new TreeNode(preorder[pre_root]);
        int leftTree_len = in_root - in_left;


        //2.使用递归,将前序和中序传递(前序中序都不算根节点),此处pre_left 换为pre_root 也可
        root.left = mybuildTree(preorder, inorder, pre_left + 1, pre_left + leftTree_len, in_left, in_root - 1);
        root.right = mybuildTree(preorder, inorder, pre_left + leftTree_len + 1, pre_right, in_root + 1, in_right);
        return root;
    }

10月25

114. 二叉树展开为链表

1.递归

前序遍历

public static  void flatten(TreeNode root) {
        List<TreeNode> list =new ArrayList<>();
        preorder(root,list);
        int size = list.size();
    //    TreeNode pre = root;
    //    TreeNode cur = root;
        for (int i = 0; i < size-1; i++) {
            TreeNode  pre=list.get(i);
            TreeNode  cur=list.get(i+1);
            pre.left=null;
            pre.right=cur;
        }
    }

    public static void preorder(TreeNode root,List<TreeNode> list) {
            if (root==null){
                return;
            }
            list.add(root);
            preorder(root.left,list);
            preorder(root.right,list);
    }

2.迭代

//poll:将首个元素从队列中弹出,如果队列是空的,就返回null
// peek:查看首个元素,不会移除首个元素
public static  void flatten(TreeNode root) {
        List<TreeNode> list = new ArrayList<>();
        Deque<TreeNode> stack=new LinkedList<TreeNode>();
        TreeNode node = root;
        while (node!=null || !stack.isEmpty()){
            while (node!=null){
                list.add(node);
                stack.push(node);
                node=node.left;
            }
            node =stack.poll();//pop()
            node=node.right;
        }
            int size=list.size();
        for (int i = 0; i < size-1; i++) {
            TreeNode pre =list.get(i);
            TreeNode cur =list.get(i+1);
            pre.left=null;
            pre.right=cur;
        }
    }

121. 买卖股票的最佳时机

public int maxProfit(int[] prices) {
        int len = prices.length;
        int min = prices[0];
        int profile = 0;
        for (int i = 0; i < len; i++) {
            if (prices[i] < min) min = prices[i];
            else profile = Math.max(profile, prices[i] - min);
        }
        return profile;
    }

11月20日

128. 最长连续序列

方法一:

sort为当前的最长连续序列长度,max为此时最长的连续序列长度。

首先给给数组排序

排序后,看num[i]与num[i+1]的关系。

如果num[i]==num[i+1],则继续循环

若num[i]+1==num[i+1],则说明连续,sort++;

否则就是不连续,sort清零

public static int longestConsecutive(int[] nums) {
        int sort;
        int max;
        if (nums.length==0 || nums==null){
            return 0;
        }else {
            sort=1;max=1;
        }
        Arrays.sort(nums);
        for (int i = 0; i < nums.length-1; i++) {
            if (nums[i]+1==nums[i+1]){
                sort++;
            }else if(nums[i]==nums[i+1]){
                    continue;
                }else{
                    max=Math.max(sort,max);
                    sort=1;
            }
        }
            return Math.max(sort,max);
    }

方法二:

题解链接

set集合可以去重

循环当前值时,若set集合中,存在比起小的连续值,则跳过。举例:set集合存在0、1,显然从0开始计算的最大序列长度更长。

public static int longestConsecutive(int[] nums) {
        Set<Integer> nums_set = new HashSet<Integer>();
        for (int num : nums) {
            nums_set.add(num);//去重
        }
        int max, sort;
        if (nums.length==0 || nums==null){
            return 0;
        }else {
            sort=1;max=1;
        }
        int curNum;
        for (int num : nums_set) {
             sort=1;
            if (nums_set.contains(num - 1)) {
                continue;//如果存在比其小的连续值,则跳过
            }
            curNum = num;
            while (nums_set.contains(curNum+1)) {
                curNum++;
                sort++;
            }
            max=Math.max(sort,max);
        }
        return max;
    }

136. 只出现一次的数字

方法一:位运算

题解

public static int singleNumber(int[] nums) {
        int sin=0;
        for (int num:nums) {
            sin^=num;
        }
            return sin;
        }

 方法二:

public static int singleNumber(int[] nums) {
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i=i+2) {
            if (i==nums.length-1){
                return nums[i];
            }
            if (nums[i]!=nums[i+1]){
                return nums[i];
            }
        }
        return 0;
    }

11月22日

139. 单词拆分

题解视频

题解文字参考

题目的含义是:能否将单词字典wordDict中每个word拼凑为s,且wordDict中的每个word可以重复使用。

单词字典wordDict中每个word相当于物品,s相当于背包。向背包中装入物品,并考虑顺序性。

//wordDict中每个word相当于物品,s相当于背包。向背包中装入物品,并考虑顺序性。
public static boolean wordBreak(String s, List<String> wordDict) {
        //set去重
        Set<String> wordSet=new HashSet<String>(wordDict);
        int n=s.length();
        boolean[] dp=new boolean[n+1];
        dp[0]=true;
        for (int i = 1; i < dp.length; i++) {
            for (String word:wordSet) {
                int len=word.length();
                if (i>=len && word.equals(s.substring(i-len,i))){
                    //装还是不装
                    dp[i]=dp[i-len]||dp[i];
                }
            }
        }
        return dp[n];
    }

11月23

141. 环形链表

方法一:哈希表

注意这个有错:

//四个样例未通过
 public boolean hasCycle(ListNode head) {
        Set<Integer> set =new HashSet<Integer>();
            ListNode cur=head;
        while (cur!=null){
            if (set.contains(cur.val)){
                return true;//有环
            }else {
                set.add(cur.val);
                cur=cur.next;
            }
        }
        return false;
    }

 错误原因:set集合中存储的是Interger类型的,若数值val一样,则视为此节点被访问过。

修改:将set集合存储的类型改为ListNode类型,val相同,但next不同,被视为不同节点。

//set中存储ListNode类型
public boolean hasCycle(ListNode head) {
        Set<ListNode> set =new HashSet<ListNode>();
        ListNode cur=head;
        while (cur!=null){
            if (set.contains(cur)){
                return true;//有环
            }else {
                set.add(cur);
                cur=cur.next;
            }
        }
        return false;
}

方法二:快慢指针

题解链接

public boolean hasCycle(ListNode head) {
        if (head==null || head.next==null){
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (fast!=slow){
            if (fast==null || fast.next==null){
                return false;
            }
            slow=slow.next;
            fast=fast.next.next;
        }
        return true;
    }

11月24日 

142. 环形链表 II

public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<ListNode>();
        ListNode cur = head;
        while(cur!=null){
            if (set.contains(cur)){
                return cur;
            }
            set.add(cur);
            cur=cur.next;
        }
        return null;
    }

146. LRU 缓存机制

题解链接

public class LRUCache {
/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
class DLinkedNode{
    int key;int value;
    DLinkedNode prev;
    DLinkedNode next;
    public DLinkedNode(){}
    public DLinkedNode(int _key,int _value){
        key=_key;
        value=_value;
    }
}
    private Map<Integer,DLinkedNode> cache = new HashMap<Integer,DLinkedNode>();
    private int size;
    private int capacity;
    private DLinkedNode head,tail;

    public LRUCache(int capacity) {
        this.size=0;
        this.capacity=capacity;
        //两个哑节点,头部和尾部
        head=new DLinkedNode();
        tail=new DLinkedNode();
        head.next=tail;
        tail.prev=head;
    }

    public int get(int key) {
        DLinkedNode node =cache.get(key);
        if (node==null){return -1;}
        //若key存在,移到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node==null){
            //若不存在,则先插入链表头部,判断链表长度,若大于容量,则删除尾部元素
            DLinkedNode newNode= new DLinkedNode(key,value);
            cache.put(key,newNode);//添加进哈希表
            addToHead(newNode);//添加至双向链表头部
            size++;//链表长度,初始化为0,还有头尾两个哑节点
            if (size>capacity){
                //如果超出容量,删除双向链表尾部节点
                DLinkedNode tail =removeTail();
                cache.remove(tail.key);
                size--;//链表长度减一
            }
        } else {
           node.value=value;
           moveToHead(node);
        }
    }


    private void removeNode(DLinkedNode node) {
        node.prev.next=node.next;
        node.next.prev=node.prev;
    }

    private void moveToHead(DLinkedNode node) {
        //先删除,再加入至头部
        removeNode(node);
        addToHead(node);
    }
    private void addToHead(DLinkedNode newNode) {
        head.next.prev=newNode;
        newNode.next=head.next;
        head.next=newNode;
        newNode.prev=head;
    }
    private DLinkedNode removeTail() {
        DLinkedNode res=tail.prev;
        removeNode(res);
        return res;
    }
}

11月25日

148. 排序链表

方法一:将链表转为数组,对数组进行排序,对链表重新赋值。

时间复杂度:O(nlogn)

空间复杂度:O(n)

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; }
 }

    public ListNode sortList(ListNode head) {
        List<Integer> list =new ArrayList<Integer>();

        ListNode cur=head;
        while (cur!=null){
            list.add(cur.val);
            cur=cur.next;
        }
        Object[] arr=list.toArray();
        Arrays.sort(arr);
        ListNode cu=head;
        for (int i = 0; i < arr.length && cu!=null; i++) {
            cu.val=(int)arr[i];
            cu=cu.next;
        }

        return head;
    }

方法二:归并排序(快慢指针)

题解

空间复杂度:O(1)

时间复杂度:O(nlogn)

补充快慢指针:

  • 寻找链表中点
    起点都为head时,我们可以让快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。
  • slow起点为head,fast起点为head.next,当程序结束时,如果链表的长度是奇数时,slow 恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏左;可以利用这个方法去进行归并排序。
  • 寻找链表的倒数第k个节点
    让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(假设 k 不会超过链表长度)

参考其他博主: 

slow起始点为1,fast起始点是2.如果fast在8,则slow停到了4。(偶数的话在中点左侧)

slow起始点为1,fast起始点是2.如果fast在7,则slow也停到了4。(奇数的话在中点)

public ListNode sortList(ListNode head) {
        if (head==null || head.next==null) {return head;}
        ListNode slow=head;
        ListNode fast=head.next;
        while (fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        ListNode tep=slow.next;//停的位置是中点(或者偶数的左侧)
        slow.next=null;

        ListNode left=sortList(head);
        ListNode right=sortList(tep);
        ListNode h=new ListNode(0);
        ListNode dummy=h;
        //合并
        while (left!=null && right!=null){
            if (left.val<=right.val){
                h.next=left;
                left=left.next;
            }else {
                h.next=right;
                right=right.next;
            }
            h=h.next;
        }
        h.next=left!=null?left:right;
        return dummy.next;
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值