LeetCode热题 HOT1-51

本文详细介绍了多种算法问题的解决方案,包括两数之和、两数相加、无重复字符的最长子串、最长回文子串、正则表达式匹配、合并两个有序链表等。通过动态规划、滑动窗口、双指针、深度优先搜索等方法,展示了如何高效地解决这些常见问题。
摘要由CSDN通过智能技术生成

1、两数之和

经典两数之和,可以通过暴力枚举解决(时间复杂度O(n^2)),当然也可以通过哈希表,hashtable.containsKey(),来选择判断x和target-x

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int m=nums.length;
        int[] re=new int [2];
        for(int i=0;i<m;i++){
            for(int j=i+1;j<m;j++){
                if(nums[i]+nums[j]==target){
                    re[0]=i;
                    re[1]=j;             
                }
            }
        }
        return re;
    }
}

2、两数相加

遍历两个链表,逐位计算它们的和

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode root = new ListNode(0);
            ListNode curNode = root;
            int carry = 0;
            while (l1 != null || l2 != null || carry != 0) {
                int l1val = l1 != null ? l1.val : 0;
                int l2val = l2 != null ? l2.val : 0;
                int sumval = l1val + l2val + carry;
                carry = sumval / 10;

                ListNode sumNode = new ListNode(sumval % 10);
                curNode.next = sumNode;
                curNode = sumNode;

                if (l1 != null) {
                    l1 = l1.next;
                }
                if (l2 != null) {
                    l2 = l2.next;
                }
            }
            return root.next;
    }
}

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

通过滑动窗口来做,start是当前字符的前一个相同字符的位置加一,然后res存储最大的长度。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //字符上一次出现的位置
        int[] last = new int[128];
        for(int i=0;i<128;i++){
            last[i] = -1;
        }
        int n = s.length();
        int res = 0;
        //滑动窗口开始的位置
        int start = 0;
        for(int i=0;i<n;i++){
            int index = s.charAt(i);
            start = Math.max(start,last[index] + 1);
            res = Math.max(res,i-start+1);
            last[index] = i;
        }
        return res;
    }
}

5、最长回文子串

找到字符串s中的最长回文子串。通过动态规划法来考虑P(i,j)=P(i+1,j−1),所以外层for循环枚举自子串的长度2-n,内层for循环枚举左边界,然后通过判断dp[i,j]的true来选择最长的子串。

class Solution {
    public String longestPalindrome(String s) {
        int size = s.length();
        boolean[][] dp = new boolean[size][size];
        int max = 1;
        int left = 0;
        int right = 0;
        if(size<2){
            return s;
        }
        for(int i=0;i<size;i++){
            dp[i][i] = true;
        }
        char[] str = s.toCharArray();
        for(int L=2;L<=size;L++){
            for(int i=0;i<size;i++){
                int j = i+L-1;
                if(j>=size){
                    break;
                }
                if(str[i]!=str[j]){
                    dp[i][j] = false;
                }else{
                    if(j-i<3){
                        dp[i][j] = true;
                    }else{
                        dp[i][j] = dp[i+1][j-1];
                    }
                }

                if(dp[i][j] && j-i+1>max){
                    max = j-i+1;
                    left = i;
                    right = j;
                }
            }  
        }
        return s.substring(left,right+1);
    }
}

10、正则表达式匹配

'.‘匹配单个字符,’*'匹配零个或多个前一位字符。动态规划来解题,dp[i,j]来表示s的前i个字符和p的前j个字符是否能够匹配,其中只有星号有特殊情况。通过判断星号代表的零个或多个字符来决定动态规划的公式。匹配0次有 dp[i,j]=dp[i,j-2],匹配多次有dp[i,j]=dp[i-1,j],让s中有星号包含的字符都得到校验。

class Solution {
    public boolean isMatch(String s, String p) {
        int m = s.length();
        int n = p.length();

        boolean[][] f = new boolean[m + 1][n + 1];
        f[0][0] = true;
        for (int i = 0; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (p.charAt(j - 1) == '*') {
                    f[i][j] = f[i][j - 2];
                    if (matches(s, p, i, j - 1)) {
                        f[i][j] = f[i][j] || f[i - 1][j];
                    }
                } else {
                    if (matches(s, p, i, j)) {
                        f[i][j] = f[i - 1][j - 1];
                    }
                }
            }
        }
        return f[m][n];
    }

    public boolean matches(String s, String p, int i, int j) {
        if (i == 0) {
            return false;
        }
        if (p.charAt(j - 1) == '.') {
            return true;
        }
        return s.charAt(i - 1) == p.charAt(j - 1);
    }
}

11、盛最多水的容器

双指针法,左右指针移动,最后得到最大的max,时间复杂度O(n)

class Solution {
    public int maxArea(int[] height) {
        int l = height.length-1;
        int i=0,j=l;
        int res = 0;
        while(i<j){
            int hgt = Math.min(height[i],height[j]);
            res = Math.max(res,hgt*(j-i));
            if(height[i]<height[j]){
                i++;
            }else{
                j--;
            }
        }
        return res;
    }
}

15、三数之和

在nums数组中选择所有的a,b,c组合,使得和为0。先对nums进行sort排序,先在第一重for循环中定义first,和前一个数重复则跳过;再定义third=n-1;在第二个for循环中定义second,起始值为first+1,和前一个数重复则跳过;三数之和大于0,third–,等于0,加入list中,直到second==third,break。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        int n = nums.length;
        Arrays.sort(nums);
        for(int first=0;first<n;first++){
            if(first>0&&nums[first]==nums[first-1]){
                continue;
            }
            int third = n-1;
            for(int second=first+1;second<n;second++){
                if(second>first+1&&nums[second-1]==nums[second]){
                    continue;
                }
                while(second<third && nums[first]+nums[third]+nums[second]>0){
                    third--;
                }
                if(second==third){
                    break;
                }
                if(nums[second]+nums[first]+nums[third]==0){
                    List<Integer> ans = new ArrayList<>();
                    ans.add(nums[first]);
                    ans.add(nums[second]);
                    ans.add(nums[third]);
                    list.add(ans);
                }
            }
        }
        return list;
    }
}

17、电话号码的字母组合

经典深度优先搜索。新建hashmap,插入key和value。在dfs中,有if判断终止条件,for循环中有res.append()和dfs()和res.deleteCharAt()。

class Solution {
    List<String> list = new ArrayList<>();
    Map<Character,String> hsp = new HashMap<>(){{
        put('2',"abc");
        put('3', "def");
        put('4', "ghi");
        put('5', "jkl");
        put('6', "mno");
        put('7', "pqrs");
        put('8', "tuv");
        put('9', "wxyz");
    }};
    public List<String> letterCombinations(String digits) {
        if(digits.length()==0){
            return list;
        }
        dfs(digits,0,new StringBuffer());
        return list;
        
    }
    void dfs(String digits,int index,StringBuffer res){
        if(index == digits.length()){
            list.add(res.toString());
            return;
        }
        char ans = digits.charAt(index);
        String lis = hsp.get(ans);
        int size1 = lis.length();
        for(int i=0;i<size1;i++){
            res.append(lis.charAt(i));
            dfs(digits,index+1,res);
            res.deleteCharAt(index);
        }

    }
}

19、删除链表的倒数第n个结点

先求出链表的总长度,然后判断是从头开始的第几个结点,之后再做处理。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode res = head;
        int m = 0;
        while(res != null){
            res = res.next;
            m++;
        }
        if(m==1) return res;
        int mi = m-n-1;
        res = head;
        int i=0;
        if(mi<0){
            return res.next;
        }
        while(i!=mi){
            res = res.next;
            i++;
        }
        res.next = res.next.next;
        return head;
    }
}

20、有效的括号

通过入栈和出栈的顺序来判断括号是否有效,s是String类型,s.toCharArray()可以转化为char数组类型。类如char == ‘(’,入栈 ‘)’ ,在判断出栈的字符即可。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        for(char c:s.toCharArray()){
            if(c=='(')  stack.push(')');
            else if(c=='{') stack.push('}');
            else if(c=='[') stack.push(']');
            else{
                if(stack.isEmpty() || stack.pop()!=c)   return false;
            }
        }
        return stack.isEmpty();
    }
}

21、合并两个有序链表

新建一个链表,当两个链表都不为空时,选择小的插入,之后选择空的链表插入。

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode listt = new ListNode();
        ListNode list = listt;
        while(list1 != null || list2 != null){
            if(list1 == null){
                list.next = list2;
                list = list.next;
                list2 = list2.next;
            }
            else if(list2 == null){
                list.next = list1;
                list = list.next;
                list1 = list1.next;
            }
            else{
                if(list1.val<list2.val){
                    list.next = list1;
                    list = list.next;
                    list1 = list1.next;
                }else{
                    list.next = list2;
                    list = list.next;
                    list2 = list2.next;
                }
            }
        }
        return listt.next;
    }
}

22、括号生成

dfs深度优先搜索,生成每一组括号时,得先生成左边的括号,再生成右边的括号。n为括号的组数,即左右括号各为n个。dfs中,if左右两边的括号都是0时,为终止条件,list.add() 。left==right时,str+“(”,left减一;left<right时,可以有两种情况。

class Solution {
    List<String> list = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        dfs("",n,n);
        return list;
    }
    void dfs(String str,int left,int right){
        if(left==0 && right==0){
            list.add(str);
            return;
        }
        if(left == right){
            dfs(str+"(",left-1,right);
        }else if(left<right){
            if(left>0){
                dfs(str+"(",left-1,right);
            }
            dfs(str+")",left,right-1);
        }
    }
}

23、合并K个升序链表

思路:新建一个总的链表res,再将其他的链表分别与这个链表合并。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode res = null;
        for(ListNode list : lists){
            res = rest(res,list);
        }
        return res;
    }
    private ListNode rest(ListNode res,ListNode list){
        ListNode ans = new ListNode();
        ListNode l = ans;
        while(res!=null && list!=null){
            if(res.val < list.val){
                ans.next = res;
                res = res.next;
            } else{
                ans.next = list;
                list = list.next;
            }
            ans = ans.next;
        }
        if(res!=null){
            ans.next = res;
            ans = ans.next;
        }
        if(list!=null){
            ans.next = list;
            ans = ans.next;
        }
        return l.next;
    }
}

31、下一个排列

寻找数组中下一个最大的排列数组,先处理完最大和最小的两种特殊情况。从右往左选择比右边的数小的数的位置n,然后在从右到n的范围内,选择第一个比n位置的数大的数的位置res。然后将n与res互换位置,再通过Arrays.sort(nums,n,size)排列。

class Solution {
    public void nextPermutation(int[] nums) {
        int size = nums.length;
        if(size == 1){
            return;
        }
        int n = size - 1;
        while(n!=0){
            if(nums[n] > nums[n-1]){
                break;
            } else{
                n--;
            }
        }
        if(n==0){
            Arrays.sort(nums);
        } else if(n==size-1){
            swap(nums,n-1,n);
        } else{
            int res = size-1;
            while(nums[res]<=nums[n-1]){
                res--;
            }
            swap(nums,n-1,res);
            Arrays.sort(nums,n,size);
        }
    }
    void swap(int[] nums,int a,int b){
        int temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }
}

32、最长有效括号

通过栈来解决问题,如果前面的 ‘)’ 符号太多,删除旧边界添加新边界,保持stack的长度为1。如果是 ‘(’ ,则入栈数组中的位置, ‘)’ 出栈,并通过 i - stack.peek() 判断最大长度。

class Solution {
    public int longestValidParentheses(String s) {
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        char[] ch = s.toCharArray();
        int size = ch.length;
        int max = 0;
        for(int i=0;i<size;i++){
            if(ch[i] == '('){
                stack.push(i);
            } else{
                if(stack.size()==1){
                    stack.pop();
                    stack.push(i);  //这里始终是删除旧边界,添加新边界
                } else{
                    stack.pop();
                    max = Math.max(max,i-stack.peek());
                }
            }
        }
        return max;
    }
}

33、搜索旋转排序数组

二分法。一个有序,另一个可能有序可能无序。

class Solution {
    public int search(int[] nums, int target) {
        int i=0,j=nums.length-1;
        if(j==0){
            if(nums[i]==target){
                return 0;
            }
            else{
                return -1;
            }
        }
        while(i<=j){
            int mid = (i+j)/2;
            if(nums[mid]==target){
                return mid;
            }
            if(nums[mid]>=nums[i]){
                if(target>=nums[i] && target<nums[mid]){
                    j=mid-1;
                }else{
                    i=mid+1;
                }
            }
            else{
                if(target>nums[mid] && target<=nums[j]){
                    i=mid+1;
                }else{
                    j=mid-1;
                }
            }
        }
        return -1;
    }
}

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

二分法,时间复杂度 O(log n) 。判断左右边界值,找到target的一个位置,在通过while左右来判断。

class Solution {
    int m = -1,n = -1;
    public int[] searchRange(int[] nums, int target) {
        int i = 0;
        int j = nums.length-1;
        int zh = (i+j)/2;
        if(j==-1){
            return new int[]{-1,-1};
        }
        rus(nums,i,j,target);
        return new int[]{n,m};
    }
    void rus(int[] nums,int i,int j,int target){
        int zh = (i+j)/2;
        if(nums[zh] < target && zh+1<=j){
            rus(nums,zh+1,j,target);
        }
        else if(nums[zh]>target && zh-1>=i){
            rus(nums,i,zh-1,target);
        }
        else if(nums[zh]==target){
            m = zh;
            n = zh;
            while(m<j && nums[m+1]==target){
                m++;
            }
            while(n>i && nums[n-1]==target){
                n--;
            }
        }
        else{
            n=-1;
            m=-1;
        }
    }
}

39、组合总和

candidates = [2,3,5], target = 8;[[2,2,2,2],[2,3,3],[3,5]];
dfs解法:target最终被减为0时,终止一次的dfs,小于0时,return;for循环中添加数组中的每个元素,ans.add(),dfs(),ans.remove()。

class Solution {
    List<List<Integer>> list = new ArrayList<>();
    List<Integer> ans = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        dfs(candidates,target,0);
        return list;
    }
    void dfs(int[] candidates,int target,int start){
        if(target==0){
            list.add(new ArrayList<>(ans));
            return;
        }
        if(target<candidates[0]){
            return;
        }
        for(int i=start;i<candidates.length;i++){
            ans.add(candidates[i]);
            dfs(candidates,target-candidates[i],i);
            ans.remove(ans.size()-1);
        }

    }
}

42、接雨水

先判断左右两边的最大高度,然后最后是左右两边的最大高度的最小值-本高度的和。

class Solution {
    public int trap(int[] height) {
        int size = height.length;
        if(size == 1){
            return 0;
        }
        int[] leftheight = new int[size];
        int[] rightheight = new int[size];
        leftheight[0] = height[0];
        rightheight[size-1] = height[size-1];
        for(int i=1;i<size;i++){
            leftheight[i] = Math.max(leftheight[i-1],height[i]);
        }
        for(int i=size-2;i>=0;i--){
            rightheight[i] = Math.max(rightheight[i+1],height[i]);
        }
        int sum = 0;
        for(int i=0;i<size;i++){
            sum += Math.min(leftheight[i],rightheight[i]) - height[i];
        }
        return sum;
    }
}

46、全排列

返回不重复数组的所有可能的全排列。dfs加回溯来解决,先建立一个list存入数组数据,再建立一个list1存list。
在这里插入图片描述

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        List<Integer> res = new ArrayList<>();
        for(int num : nums){
            res.add(num);
        }
        int n = nums.length;
        dfs(n,0,list,res);
        return list;

    }
    void dfs(int n,int first,List<List<Integer>> list,List<Integer> res){
        if(first == n){
            list.add(new ArrayList<>(res));
            return;
        }
        
        for(int i=first;i<n;i++){
            Collections.swap(res,first,i);
            dfs(n,first+1,list,res);
            Collections.swap(res,first,i);
        }
    }
}

48、旋转图像

不要想着一次性的旋转好,先在矩阵上下掉转,然后在斜对角线掉转。

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        for(int i=0;i<n/2;i++){
            for(int j=0;j<n;j++){
                swap(matrix,i,j,n-i-1,j);
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                swap(matrix,i,j,j,i);
            }
        }
    }
    void swap(int[][] matrix,int i,int j,int x,int y){
        int temp = matrix[i][j];
        matrix[i][j] = matrix[x][y];
        matrix[x][y] = temp;
    }
}

49、字母异位词分组

把string数组中的每个str转化为char数组,对char数组进行排序后作为key,str作为value。依次返回每个key的value。其中用到的 String key = String.valueOf(ch),将char数组转化为String类型;map.get(key).add(str);new ArrayList(map.values())。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String,ArrayList<String>> map = new HashMap<>();
        for(String str : strs){
            char[] ch = str.toCharArray();
            Arrays.sort(ch);
            String key = String.valueOf(ch);
            if(!map.containsKey(key)){
                map.put(key,new ArrayList<>());
            }
            map.get(key).add(str);
        }
        return new ArrayList(map.values());
    }
}

53、最大子数组和

动态规划,类似滚动数组思想。

class Solution {
    public int maxSubArray(int[] nums) {
        int max = nums[0];
        int sum = 0;
        for(int num : nums){
            sum = Math.max(sum+num,num);
            max = Math.max(max,sum);
        }
        return max;
    }
}

55、跳跃游戏

maxplace为当前位置所能跳到的最远距离的位置,将他和每个点到的最远位置对比。直到最远位置大于等于终点即可返回true,否则false。

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

56、合并区间

先排序,再处理,最后合并。

class Solution {
    public int[][] merge(int[][] intervals) {
        int size = intervals.length;
        if(size == 1){
            return intervals;
        }
        // 重新排序
        Arrays.sort(intervals,new Comparator<int[]>(){
            public int compare(int[] interval1,int[] interval2){
                return interval1[0] - interval2[0];
            }
        });
        int count = 0;
        for(int i=1;i<size;i++){
            // [1,5]  [3,7]   ==>  [1,5]  [5,7] 
            if(intervals[i][0] <= intervals[i-1][1] && intervals[i-1][1] < intervals[i][1]){
                intervals[i][0] = intervals[i-1][1];
                count++;
            }
            // [1,5]  [3,4]   ==>  [1,5]  [5,5] 
            else if(intervals[i][0] <= intervals[i-1][1] && intervals[i-1][1] >= intervals[i][1]){
                intervals[i][0] = intervals[i-1][1];
                intervals[i][1] = intervals[i-1][1];
                count++;
            }
        }
        int[] ans = new int[size*2];
        int z = 0;
        // 将所有数存入ans中
        for(int i=0;i<size;i++){
            ans[z++] = intervals[i][0];
            ans[z++] = intervals[i][1];
        }
        int[][] in = new int[(size-count)][2];
        int m = 0;
        for(int i=0;i<size-count;i++){
            in[i][0] = ans[m];
            m++;
            // 重复的跳转
            while(m<ans.length-1 && ans[m]==ans[m+1]){
                m = m+2;
            }
            in[i][1] = ans[m];
            m++;
        }
        return in;
    }
}

62、不同路径

动态规划,先给上左两边赋初始值1,到某个点的路径总数是上面点的路径总数+左边点的路径总数。

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

64、最小路径和

动态规划,先给上左付初始值,到每个点的最小距离都是这个点上左的最小距离的最小值+这个点的距离。

class Solution {
    public int minPathSum(int[][] grid) {
        int high = grid.length;
        int width = grid[0].length;
        for(int i=1;i<high;i++){
            grid[i][0] += grid[i-1][0];
        }
        for(int j=1;j<width;j++){
            grid[0][j] += grid[0][j-1];
        }
        for(int i=1;i<high;i++){
            for(int j=1;j<width;j++){
                grid[i][j] += Math.min(grid[i-1][j],grid[i][j-1]);
            }
        }
        return grid[high-1][width-1];
    }
}

70、爬楼梯

class Solution {
    public int climbStairs(int n) {
        int[] dp = new int[100000];
        dp[0] = 1;
        dp[1] = 2;
        for(int i=2;i<n;i++){
            dp[i] = dp[i-2] + dp[i-1];
        }
        return dp[n-1];
    }
}

72、编辑距离

word1 = “horse”, word2 = “ros”,返回将 word1 转换成 word2 所使用的最少操作数。
dp数组表示word1的前i个字母转化为word2的前j个字母所使用的最少操作。
如果当前字母相同,则dp[i,j] = dp[i-1,j-1];否则是增、删、替三个操作的最小值+1。
其中增加为dp[i,j] = dp[i,j-1] + 1;就相当于原来是i-1,j-1,现在word1增加了一个,所以比较i,j-1。

class Solution {
    public int minDistance(String word1, String word2) {
        int m1 = word1.length();
        int m2 = word2.length();
        int[][] dp = new int[m1+1][m2+1];
        for(int i=1;i<=m1;i++){
            dp[i][0] = dp[i-1][0] + 1;
        }
        for(int j=1;j<=m2;j++){
            dp[0][j] = dp[0][j-1] + 1;
        }
        for(int i=1;i<=m1;i++){
            for(int j=1;j<=m2;j++){
                if(word1.charAt(i-1)==word2.charAt(j-1)){
                    dp[i][j] = dp[i-1][j-1];
                }
                else{
                    dp[i][j] = Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j])) + 1;
                }
            }
        }
        return dp[m1][m2];
    }
}

75、颜色分类

随便的一个排序算法,这里是冒泡排序。

class Solution {
    public void sortColors(int[] nums) {
        for(int i=0;i<nums.length-1;i++){
            for(int j=0;j<nums.length-i-1;j++){
                if(nums[j]>nums[j+1]){
                    int temp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = temp;
                }
            }
        }
    }
}

76、最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。
先把t中的所有字符都在need数组中计数,然后滑动窗口的思想来计算最小值。

class Solution {
    public String minWindow(String s, String t) {
        int[] need = new int[128];
        for(int i=0;i<t.length();i++){
            need[t.charAt(i)]++; 
        }
        int l=0, r=0, size=Integer.MAX_VALUE, count=t.length(), start=0;
        while(r<s.length()){
            char c = s.charAt(r);
            if(need[c]>0){
                count--;
            }
            need[c]--;
            if(count==0){
                //移掉左边没用的
                while(l<r && need[s.charAt(l)]<0){
                    need[s.charAt(l)] ++;
                    l++;
                }
                //替换最大值
                if(r-l+1 < size){
                    size = r-l+1;
                    start = l;
                }
                //记录完这个size后,l+1
                need[s.charAt(l)]++;
                l++;
                count++;
            }
            r++;
        }
        return size == Integer.MAX_VALUE ? "" : s.substring(start,start+size);
    }
}

78、子集

dfs加回溯。

class Solution {
    List<List<Integer>> list = new ArrayList<>();
    List<Integer> ans = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums,0);
        return list;
    }

    void dfs(int[] nums,int start){
        list.add(new ArrayList<>(ans));
        if(start>=nums.length){
            return;
        }
        for(int i=start;i<nums.length;i++){
            ans.add(nums[i]);
            dfs(nums,i+1);
            ans.remove(ans.size()-1);
        }
    }
}

79、单词搜索

dfs加回溯。

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                if(dfs(board,words,i,j,0)) return true;
            }
        }
        return false;
    }
    boolean dfs(char[][] board,char[] word,int i, int j, int k){
        if(i>=board.length || j>=board[0].length || i<0 || j<0 || board[i][j] != word[k]){
            return false;
        }
        if(k==word.length-1)    return true;
        board[i][j]='\0';
        boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);

        board[i][j]=word[k];
        return res;

    }
}

84、柱状图中最大的矩形

栈中下一个柱体就是左边第一个小于自身的柱体。
area始终算的是i之前的最大面积。

// 在一维数组中,找第一个比自己小的元素的位置。
class Solution {
    public int largestRectangleArea(int[] heights) {
        // 这里为了代码简便,在柱体数组的头和尾加了两个高度为 0 的柱体。
        int[] tmp = new int[heights.length + 2];
        System.arraycopy(heights, 0, tmp, 1, heights.length);

        Deque<Integer> stack = new ArrayDeque<>();
        int area = 0;
        for (int i = 0; i < tmp.length; i++) {
            // 对栈中柱体来说,栈中的下一个柱体就是其「左边第一个小于自身的柱体」;
            // 若当前柱体 i 的高度小于栈顶柱体的高度,说明 i 是栈顶柱体的「右边第一个小于栈顶柱体的柱体」。
            // 因此以栈顶柱体为高的矩形的左右宽度边界就确定了,可以计算面积
            while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
                int h = tmp[stack.pop()];
                area = Math.max(area, (i - stack.peek() - 1) * h);
            }
            stack.push(i);
        }

        return area;
    }
}

85、最大矩形

和上题类似,只不过要统计出每列1的个数。

class Solution {
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }
        int sx = matrix.length;
        int sy = matrix[0].length;
        int[] heights = new int[sy];
        int ans = 0;
        for(int i=0;i<sx;i++){
            for(int j=0;j<sy;j++){
                if(matrix[i][j] == '1'){
                    heights[j]++;
                } else{
                    heights[j] = 0;
                }
            }
            ans = Math.max(ans,largestRectangleArea(heights));
        }
        return ans;
    }

    public int largestRectangleArea(int[] heights) {
        int[] tmp = new int[heights.length + 2];
        System.arraycopy(heights, 0, tmp, 1, heights.length);

        Deque<Integer> stack = new ArrayDeque<>();
        int area = 0;
        for (int i = 0; i < tmp.length; i++) {
            while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
                int h = tmp[stack.pop()];
                area = Math.max(area, (i - stack.peek() - 1) * h);
            }
            stack.push(i);
        }

        return area;
    }
}

94、二叉树的中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        mid(root,list);
        return list;
    }
    void mid(TreeNode root,List<Integer> list){
        if(root == null){
            return;
        }
        mid(root.left,list);
        list.add(root.val);
        mid(root.right,list);
    }
}

96、不同的二叉搜索树

动态规划,可以分解为左子树和右子树。

class Solution {
    public int numTrees(int n) {
        int[] res = new int[n+1];
        res[0] = 1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                res[i] += res[j-1] * res[i-j];
            }
        }
        return res[n];
    }
}

98、验证二叉搜索树

通过二叉搜索树的特点来解答,节点的左子树只包含小于当前节点的数,节点的右子树只包含大于当前节点的数。

class Solution {
    public boolean isValidBST(TreeNode root) {
        return todo(root,Long.MIN_VALUE,Long.MAX_VALUE);
    }
    boolean todo(TreeNode root,long low,long high){
        if(root == null){
            return true;
        }
        if(root.val<=low || root.val>=high){
            return false;
        }
        else{
            return todo(root.left,low,root.val) && todo(root.right,root.val,high);
        }
    }
}

101、对称二叉树

递归

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left,root.right);
    }
    boolean compare(TreeNode left,TreeNode right){
        if((left!=null&&right!=null)&&left.val==right.val){
            return compare(left.left,right.right) && compare(left.right,right.left);
        }
        else if(left==null && right==null){
            return true;
        }
        else{
            return false;
        }
    }
}

102、二叉树的层序遍历

广度优先搜索,每个节点都入队和出队一次,所以时间复杂度为O(n)。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list1= new ArrayList<List<Integer>>();
        if(root == null){
            return list1;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> list2= new ArrayList<Integer>();
            int size = queue.size();
            for(int i=1;i<=size;i++){
                TreeNode node = queue.poll();
                list2.add(node.val);
                if(node.left != null){
                    queue.add(node.left);
                }
                if(node.right != null){
                    queue.add(node.right);
                }
            }
            list1.add(list2);
        }
        return list1;
    }
}

104、二叉树的最大深度

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        else{
            int leftmax = maxDepth(root.left);
            int rightmax = maxDepth(root.right);
            return Math.max(leftmax,rightmax)+1;
        }

    }
    
}

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

先判断每个根节点的位置,然后再分割左右子树,递归来做。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length==0 || inorder.length==0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        for(int i=0;i<preorder.length;i++){
            if(preorder[0] == inorder[i]){
                // 前序遍历:根左右
                // 中序遍历:左根右
                root.left = buildTree(Arrays.copyOfRange(preorder,1,i+1),
                            Arrays.copyOfRange(inorder,0,i));
                root.right = buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),
                            Arrays.copyOfRange(inorder,i+1,inorder.length));
                break;
            }
        }
        return root;
    }
}

114、二叉树展开为链表

将二叉树转化为前序遍历的list之后,在通过list顺序转化为链表顺序。

class Solution {
    List<TreeNode> list = new ArrayList<>();
    public void flatten(TreeNode root) {
        res(root);
        for(int i=1;i<list.size();i++){
            TreeNode prev = list.get(i-1);
            TreeNode ans = list.get(i);
            prev.left = null;
            prev.right = ans;
        }
        
    }
    void res(TreeNode root){
        if(root == null){
            return;
        }
        list.add(root);
        res(root.left);
        res(root.right);
    }
}

121、买卖股票的最佳时机

买卖股票:最大值:当天的值减去最小值 和 历史最大值 的比较
最小值:当天的值和历史最小值的比较。

class Solution {
    public int maxProfit(int[] prices) {
        //动态规划,dp思想
        int l1 = prices.length;
        int max =0;
        int min = prices[0];
        for(int i=0;i<l1;i++){
            //最大值为 i-1天的最大值 与 i天减之前最小值 的比较
            max=Math.max(max,prices[i]-min);
            //最小值为 i天及以前的最小值比较
            min=Math.min(min,prices[i]);
        }
        return max;
    }
}

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

其中的递归函数中,返回值是:本节点值nums 加上 左右节点的递归函数中的最大值。
在递归函数中,用max记录最大路径和。

class Solution {
    int max = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        int a = count(root);
        return max;
    }
    int count(TreeNode root){
        if(root == null){
            return 0;
        }
        int num = root.val;
        int left = Math.max(count(root.left),0);
        int right = Math.max(count(root.right),0);
        max = Math.max(max,num+left+right);
        return num + Math.max(left,right);
    }
}

128、最长连续序列

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

136、只出现一次的数字

异或运算

class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;
        for(int num : nums){
            res ^= num;
        }
        return res;
    }
}

139、单词拆分

动态规划。先通过 new HashSet(wordDict) 把字典中的单词写入hashset中,然后建立dp数组,如果dp[i] = true,说明第
i个以及之前的字符都能够得到匹配。

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordDictSet = new HashSet(wordDict);
        boolean[] dp = new boolean[s.length()+1];
        dp[0] = true;
        for(int i=1;i<=s.length();i++){
            for(int j=0;j<i;j++){
                if(dp[j] && wordDictSet.contains(s.substring(j,i))){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

141、环形链表

这里通过快慢指针来做,快指针走两步,慢指针走一步,如果快慢指针相同,则返回true。
本题还可以通过hashset来做。

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

142、环形链表 II

和上题类似,还要判断入环点的位置。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null){
            return null;
        }
        ListNode fast = head.next;
        ListNode slow = head;
        Set<ListNode> hst = new HashSet<>();
        while(fast != slow){
            if(fast == null || fast.next == null){
                return null;
            }
            hst.add(slow);
            fast = fast.next.next;
            slow = slow.next;
        }
        while(!hst.contains(slow)){
            hst.add(slow);
            slow = slow.next;
        }
        return slow;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值