Hot100方法及易错点总结1

一、

1.两数之和

定义数组

int[] arr = new int[5]; 
int[] arr = {1, 2, 3, 4, 5};
int[] arr = new int[]{1, 2, 3, 4, 5}; 

前面的是类型[]在前面,后面的是new 类型[]在后面

49字母移位词

HashMap<String,List<String>>map =new HashMap();
HashMap<String, String> map = new HashMap<>(); 

这篇得全篇背诵好多经典句子

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String,List<String>>map =new HashMap();
        for(int i=0;i<strs.length;i++)
        {
            char[] charArray = strs[i].toCharArray();
            Arrays.sort(charArray);
            String sortedStr = new String(charArray);
            if(map.containsKey(sortedStr))
            {
               List list= map.get(sortedStr);
               list.add(strs[i]);
               map.put(sortedStr,list);
            }
            else
            {
                List<String>list =new ArrayList<>();
                list.add(strs[i]);
                map.put(sortedStr,list);
            }
        }
        return new ArrayList<List<String>>(map.values());
    }
}
1.list操作数是add
 list.add(strs[i]);
2.map操作数是put
map.put(sortedStr,list);
3.创建对象记得要new,并且创建的是ArrayList
 return new ArrayList<List<String>>(map.values());

128.最长连续序列

class Solution {
    public int longestConsecutive(int[] nums) {
        HashSet<Integer>set =new HashSet();
        for(int num:nums)
        {
            set.add(num);
        }
        int max_len=0;
        for(int num:set)
        {
            if(!set.contains(num-1))
            {//是起始位置
                int len =1;
                while(set.contains(++num))
                {//有连续的序列
                
                    len++;
                }
                max_len=Math.max(len,max_len);
            }

            

        }
        return max_len;
    }
}

易错点:
1.遍历的是set集合
2.set.contains(++num)
查看num的后一个元素是否存在,所以首先num要+1,并且,存在还要继续判断num+1的值

283.移动零

思路就是,遇到非零元素,往前放置,最后的全部补0就行,使用快慢指针来实现

class Solution {
    public void moveZeroes(int[] nums) {
        //一个定位,一个用来遍历
        int fast=0;
        int slow=0;
        while(fast<nums.length)
        {
            if(nums[fast]!=0)
            {
                nums[slow]=nums[fast];
                slow++;
                fast++;
            }
            else
            {
                fast++;
            }
        }
        for(int i=slow;i<nums.length;i++)
        {
            nums[i]=0;
        }
    }
}

11.承最多水的容器

class Solution {
    public int maxArea(int[] height) {
        int left =0;
        int right =height.length-1;
        int max_area=(right-left)*Math.min(height[left],height[right]);
        int area=max_area;
        while(left<right)
        {
           
           if(height[left]<height[right])
           {
             left++;
           }
           else
           {
            right--;
           }
           
            area =(right-left)*Math.min(height[left],height[right]);
            max_area=Math.max(area,max_area);
        }
        return max_area;
    }
}
1.易错点:
不能写成
while(left<height.length&&right>=0;left<right)

这种情况没有固定住一边,两边都在移动。必须得固定住最大边

15.三数之和

两数之和会做,三数之和就是两数之和的拓展

思路:
首先对数组进行排序,排序后固定一个数 nums[i],再使用左右指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0

(1)如果 nums[i]大于 0,则三数之和必然无法等于 0,结束循环sum
(2)sum=0,满足则添加进结果集
(3)sum>0,right–
(4)sum<0,left++;
2.重复:
(1)如果 nums[i] == nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过
(2)当 sum == 0 时,nums[L] == nums[L+1] 则会导致结果重复,应该跳过,L++
(3)当 sum == 0 时,nums[R] == nums[R−1] 则会导致结果重复,应该跳过,R−−

注意:这道题如果没有去掉重复的测试的时候也会超出内存限制

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);  // 排序数组

        for (int i = 0; i < nums.length; i++) {
            // 跳过重复的元素,避免重复三元组
            if (nums[i]>0)
            {
                continue;
            }
            
            
            if (nums[i] > 0) {
                break;  // 如果当前值大于 0,后面的和一定大于 0,直接结束
            }

            int left = i + 1, right = nums.length - 1;

            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];

                if (sum == 0) {
                    if(i>0&& nums[i] == nums[i - 1])
                    {
                        break;
                    }
                    List<Integer> triplet = new ArrayList<>();
                    triplet.add(nums[i]);
                    triplet.add(nums[left]);
                    triplet.add(nums[right]);
                    list.add(triplet);

                    // 跳过重复的元素
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }

                    // 移动指针
                    left++;
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }

        return list;
    }
}

42.盛水最多的容器

在这里插入图片描述
灵神讲的非常好
每个位置都当作一个水桶来进行操作。

class Solution {
    public int trap(int[] height) {
        int []pre=new int [height.length];
        int []after=new int [height.length];
        int max_pre=0;
        int max_after=0;
        for(int i=0;i<height.length;i++)
        {
            max_pre=Math.max(max_pre,height[i]);
            pre[i]=max_pre;

        }
         for(int i=height.length-1;i>=0;i--)
        {
            max_after=Math.max(max_after,height[i]);
            after[i]=max_after;
        }
        int sum =0;
        for(int i=0;i<height.length;i++)
        {
            int v=Math.min(after[i],pre[i])-height[i];
            if(v>0)
            {
                sum+=v;
            }
        }
        return sum;

    }
}
易错点:
1.int v=Math.min(after[i],pre[i]),不是after[i]-pre[i],水桶能装的水取决于最短的边
class Solution {
    public int trap(int[] height) {
        int max_pre = 0;  // 左边的最大高度
        int max_after = 0;  // 右边的最大高度
        int sum = 0;  // 存储雨水总量
        int left = 0;  // 左指针
        int right = height.length - 1;  // 右指针

        while (left < right) {
            // 更新左侧最大值max_pre 和 右侧最大值max_after
            max_pre = Math.max(max_pre, height[left]);
            max_after = Math.max(max_after, height[right]);

            // 如果左边的最大值大于右边的最大值,相当于我们把右边的作为容器,右边的已经计算完了,自然要左移
            if (max_pre > max_after) {
                // 计算当前右侧水量
                int v = max_after - height[right];
                if (v > 0) {
                    sum += v;  // 累加水量
                }
                right--;  // 右指针左移
            } else {
                // 计算当前左侧水量
                int v = max_pre - height[left];
                if (v > 0) {
                    sum += v;  // 累加水量
                }
                left++;  // 左指针右移
            }
        }

        return sum;  // 返回总水量
    }
}


1.取决于水桶最短的边
2. 如果左边的最大值大于右边的最大值,相当于我们把右边的作为容器,右边的已经计算完了,自然要左移(这个左右移动的逻辑千万不能错误)

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

思路:
求以nums[i]为最后一个元素的滑动窗口,每个元素都这样求。所有滑动窗口的最大值就是最长的字串

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashSet<Character> set = new HashSet<>();
        int left = 0;
        int right = 0;
        int s_len = s.length();
        int length = 0;
        int max_length = 0;
        
        while (right < s_len) {
            char a = s.charAt(right);
            if (!set.contains(a)) {
                set.add(a);
                length++;
                right++;
            } else {
                // 内层循环,移除左边字符直到不包含重复字符
                char b = s.charAt(left);
                set.remove(b);
                left++;
                length--;
            }
            max_length = Math.max(length, max_length);
        }
        
        return max_length;
    }
}


二、

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

给定两个字符串 s 和 p,找到 s 中所有 p 的
异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

思路分析:统计p中每个字符出现的次数,最多26个字符。
如果s的子窗口中字符出现的次数和p中一样,那就是一个字母移位词

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list =new ArrayList<>();
        int s_len =s.length();
        int p_len =p.length();
        int[] p_count =new int[26];
        int[] windows =new int[26];
        if (s_len < p_len) {
            return list;
        }
        for(int i =0;i<p_len;i++)
        {
           p_count[p.charAt(i)-'a']++; 
        }
        int left =0;int right= 0;
        while (right < s_len) {
            // 更新右边字符
            windows[s.charAt(right) - 'a']++;

            // 窗口大小达到 p_len 时
            if (right - left + 1 == p_len) {
                if (matches(windows, p_count)) {
                    list.add(left);
                }
                // 移除左边字符,窗口滑动
                windows[s.charAt(left) - 'a']--;
                left++;
                right++;
            }
            else if(right - left + 1 < p_len)
            {
               right++; 
            }
           
            
        }
        return list;
    }
    public boolean matches(int []a,int[]b)
    {
        for(int i=0;i<26;i++)
        {
            if(a[i]!=b[i])
            {
                return false;
            }

        }
        return true;
    }
}

别看感觉简单,但是实际很多小细节,需要自己仔细做
1.right - left + 1 == p_len时,就需要缩减窗口了,不可能出现right - left + 1 > p_len的情况
长时间的解法:

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list =new ArrayList<>();
        int s_len =s.length();
        int p_len =p.length();
        int[] p_count =new int[26];
        int[] windows =new int[26];
        if (s_len < p_len) {
            return list;
        }
        for(int i =0;i<p_len;i++)
        {
           p_count[p.charAt(i)-'a']++; 
        }
        int left =0;int right= 0;
        while (right < s_len) {
            // 更新右边字符
            windows[s.charAt(right) - 'a']++;

            // 窗口大小达到 p_len 时
            if (right - left + 1 == p_len) {
                if (matches(windows, p_count)) {
                    list.add(left);
                }
                // 移除左边字符,窗口滑动
                windows[s.charAt(left) - 'a']--;
                left++;
            }
            // 移动右边界
            right++;
        }
        return list;
    }
    public boolean matches(int []a,int[]b)
    {
        for(int i=0;i<26;i++)
        {
            if(a[i]!=b[i])
            {
                return false;
            }

        }
        return true;
    }

每次都要更新右边的字符,只有当等号成立的时候才需要考虑其他需要修改的情况,这个思维模式得学会

560.和为k的子数组

class Solution {
    public int subarraySum(int[] nums, int k) {
        int count =0;
        for(int i =0;i<nums.length;i++)//i代表子数组的末尾
        {
            int sum =0;
            for(int j=i;j>=0;j--)//j代表子数组的开头
            {
                sum =sum+nums[j];
                if(sum==k)
                {
                    count++;
                }
            }
        }
        return count;
    }
}
思想还是滑动窗口的思想。

239.滑动窗口最大值

import java.util.Deque;
import java.util.LinkedList;

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) return new int[0];
        
        Deque<Integer> deque = new LinkedList<>();
        int[] output = new int[nums.length - k + 1];
        int outputIndex = 0;

        int left = 0, right = 0;
        
        while (right < nums.length) {
            // 保持 deque 递减
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[right]) {
                deque.removeLast();
            }
            // 加入当前元素的索引
            deque.addLast(right);
            

            // 窗口大小达到 k 后,记录最大值
            if (right - left + 1 == k) {
                output[outputIndex++] = nums[deque.peekFirst()]; // 队首是最大值
                // 如果队首元素已不在窗口范围内,移除队首
                if (deque.peekFirst() == left) {
                    deque.removeFirst();
                }
                left++; // 滑动窗口左边界
            }
            right++; // 滑动窗口右边界
        }
        return output;
    }
}

易错点,这里队列里存储的是 数值的索引,而不是具体的值,原因是这样可以更好的根据left判断出来是否超出滑动窗口的范围。

如果deque存储的是具体的值
deque.peekFirst() == nums[left]
只是判断了这俩数值是否相等,不能说明已经超出了滑动窗口的界限,所以这个逻辑是不正确的。

53.最大子数组和

以第i个元素结尾的最大子数组和dp[i]
dp[i]=max((dp[i-1]+nums[i]),dp[i])理解为:
要么加上这个元素,要么从头开始作为字串。

class Solution {
    public int maxSubArray(int[] nums) {
        int []dp=new int[nums.length];
        dp[0]=nums[0];
        int max=dp[0];
        for(int i=1;i<nums.length;i++)
        {
            dp[i]=Math.max((dp[i-1]+nums[i]),nums[i]);
            max=Math.max(max,dp[i]);
        }
        return max;

    }
}

三、

76.最小覆盖字串(未)

解决方案

题目:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。

思路:先找全,再缩小左边界

56.合并区间

1.首先要对区间进行排序,可以按照左边界排序,或者右边界排序
2.两个区间是否重合(重合的条件是:下一个区间的左边界<上一个区间的右边界)

需要注意的点:
1.Arrays.sort(intervals,(a.b)->a[0]-b[0]);//这是升序排序
2.list.get(list.size()-1)//时间复杂度是O(1)
list.get()时间复杂度是O(1)
list.size()时间复杂度也是O13.  List<int[]>list =new ArrayList<>();//链表中每个元素都是int[]整型数组
class Solution {
    public int[][] merge(int[][] intervals) {
        List<int[]>list =new ArrayList<>();
       
        Arrays.sort(intervals,(a,b)->a[0]-b[0]);
        list.add(new int[]{intervals[0][0], intervals[0][1]});
        for(int i=1;i<intervals.length;i++)
        {
           
            if(intervals[i][0]<=list.get(list.size()-1)[1])
            {
                
                list.get(list.size()-1)[1]=Math.max(intervals[i][1],list.get(list.size()-1)[1]);
                
            }
            else
            {
                list.add(new int[]{intervals[i][0], intervals[i][1]});
            }

            
        }
        return list.toArray(new int[list.size()][]);
    }
}

189.轮转数组

第二次写竟然思路要比用官方的方法写思路简单!!!

  //k代表移动的次数,n代表数组的长度
  //思路是:移动后k次后,实际相当于移动为k%n。
  //前k%n的元素变为后n-k元素,后n-k变为前k%n的元素
  //原来的nums[0],变为answer[k%n]
class Solution {
    public void rotate(int[] nums, int k) {
       
       int []answer =new int[nums.length];
       //原来nums第一个元素的索引从k开始
       int nums_index=0;
       for(int i=k%nums.length;i<answer.length;i++)
       {
        answer[i]=nums[nums_index++];
       }
       //nums_index继续用着就行
       for(int i=0;i<k%nums.length;i++)
       {
        answer[i]=nums[nums_index++];
       }
       for(int i=0;i<nums.length;i++)
       {
        nums[i]=answer[i];
       }
       
    }
}

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

思路:用前后缀来解题

class Solution {
    public int[] productExceptSelf(int[] nums) {
       //算出一个数的前缀和后缀的乘积
       int []pre=new int[nums.length];
       int []after =new int[nums.length];
       pre[0]=1;after[nums.length-1]=1;
       for(int i=1;i<nums.length;i++)
       {
        pre[i]=pre[i-1]*nums[i-1];
       }
       for(int i=nums.length-2;i>=0;i--)
       {
        after[i]=after[i+1]*nums[i+1];
       }
       for(int i=0;i<nums.length;i++)
       {
        nums[i]=pre[i]*after[i];
       }
       return nums;
    }
}
after[i]=after[i+1]*nums[i+1];
 pre[i]=pre[i-1]*nums[i-1];
 //i要用after和pre的下角标来,不然容易出错。

41.缺失的第一个正数

做题思路

1.需要交换和不需要交换的元素进行分类
class Solution {
    public int firstMissingPositive(int[] nums) {
        int index = 0;
        while (index < nums.length) {
            // 1.需要交换的元素 必须>0 2.需要交换的元素必须<=数组长度 3.需要交换的两个元素不相等
            if (nums[index] > 0 && nums[index] <= nums.length && nums[nums[index] - 1] != nums[index]) {
                
                // 交换 nums[index] 和 nums[nums[index] - 1]
                int tmp = nums[nums[index] - 1];
                nums[nums[index] - 1] = nums[index];
                nums[index] = tmp;
            } else {
                // 如果当前元素不需要交换,直接移动到下一个
                index++;
            }
        }

        // 查找第一个不符合条件的位置
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        // 如果所有位置都符合条件,那么缺失的最小正整数就是 nums.length + 1
        return nums.length + 1;
    }
}

73.矩阵置零

1.标记出来哪里有0OK
class Solution {
    public void setZeroes(int[][] matrix) {
        int []row=new int[matrix.length];//m
        int []col =new int [matrix[0].length];//n
        for(int i=0;i<row.length;i++)
        {
            for(int j=0;j<col.length;j++)
            {
                if(matrix[i][j]==0)
                {
                    row[i]=1;
                    col[j]=1;
                }
            }
        }
         for(int i=0;i<row.length;i++)
        {
            if(row[i]==1)
            {
                for(int j=0;j<col.length;j++)
                {
                    matrix[i][j]=0;
                }
            }
        }
        for(int j=0;j<col.length;j++ )
        {
            if(col[j]==1)
            {
                for(int i=0;i<row.length;i++)
            {
                 matrix[i][j]=0;
            }

            }
            
        }

    }
}

54.螺旋矩阵

1.每次处理的规则必须一致,是[)或者[]必须一致。(代码随想录的语言)
2.判断是否还要从右往左,以及判断是否从下往上,需要判断先决条件
(因为写在一个for循环里了。必须要多判断一次)
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer>list =new ArrayList<Integer>();
        int left=0;
        int right =matrix[0].length-1;
        int top=0;
        int bottom =matrix.length-1;
        while(left<=right&&top<=bottom)
        {
            //从左到右
            for(int i=left;i<=right;i++ )
            {
                list.add(matrix[top][i]);
            }
            top++;
            //从上到下
            for(int i=top;i<=bottom;i++)
            {
                list.add(matrix[i][right]);
            }
            right--;
            //从右到左
            if(top<=bottom)
            {
                 for(int i=right;i>=left;i--)
                {
                    list.add(matrix[bottom][i]);
                }
                bottom--;
            }
           
            
            //从下到上
            if(left<=right)
            {
                for(int i =bottom;i>=top;i--)
                {
                    list.add(matrix[i][left]);
                }
            left++;

            }
            
        }
        return list;
    }
}

四、

48.旋转图像

在这里插入图片描述
解题思路:观察图片发现 1,2,3 和123,很像是沿着副对角线翻转得来的,但是是逆序,所以还需要列和列之间进行交换。

易错点:沿着副对角线翻转,只需要翻转上半个三角,不能翻转下半三角
class Solution {
    public void rotate(int[][] matrix) {
        //首先将最后面的列放到第一列,列之间两两交换
        //在沿着副对角线交换元素
        int left=0;int right=matrix[0].length-1;
        while(left<right)
        {
            for(int i=0;i<matrix.length;i++)
            {
                swap_left_right(matrix,i,left,right);
            }
            left++;
            right--;
        }
        for(int i=0;i<matrix.length;i++)//行
        {
            for(int j=0;j<matrix.length-i;j++)//列
            {
                swap_i_j(matrix,i,j);
            }
        }
    }
     public void swap_left_right(int[][]matrix,int i,int left,int right)
    {
        int tmp=matrix[i][right];
        matrix[i][right]=matrix[i][left];
        matrix[i][left]=tmp;
    }
    public void swap_i_j(int[][]matrix,int i,int j)
    {
        int tmp=matrix[i][j];
        matrix[i][j]=matrix[matrix.length-1-j][matrix.length-1-i];
        matrix[matrix.length-1-j][matrix.length-1-i]=tmp;
    }
}

240.搜索二维矩阵II

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
       int bottom=matrix.length-1;//0
       int right=matrix[0].length-1;//0
       int row_index=0;
       int col_index=0;
      
       while(row_index<=bottom&&col_index<=right)
       {
            while(right>=0&&target<=matrix[row_index][right])
        {
            if(target==matrix[row_index][right])return true;
            right--;
        }
        row_index++;
        //target>matrix[row_index][right]  5>matrix[0][1]
        if(row_index<=bottom&&col_index<=right)
        {
            while(row_index<=bottom&&target>=matrix[row_index][right])
            {
                if(target==matrix[row_index][right])return true;
                row_index++;
            }
            right--;
        //target<matrix[row_index][right]
        }
        
       }   
       return false;
    }
}

1.虽然这次也很慢,但是这次起码答题思路正确
2.while(right>=0&&target<=matrix[row_index][right])
        {
            if(target==matrix[row_index][right])return true;
            right--;
        }
//这里right>=0必须得有,因为right很可能<0了,但是matrix[row_index][right]还得计算

五、

160.相交链表

1.易错点:while循环写的时候容易不更新条件,导致索引越界
2.从第一个元素开始比对,所以不是p.next
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null)return null;
        int count_A=0;int count_B=0;
        ListNode p= headA;ListNode q= headB;
        while(p!=null)
        {
            count_A++;
            p=p.next;
        }
         while(q!=null)
        {
            count_B++;
            q=q.next;
        }
        if( count_A>count_B)//A比较长点
        {
           
            int count=(count_A-count_B);
            while(count>0)
            {
                headA=headA.next;
                count--;
            }
        }
        else
        {
            int count=(count_B-count_A);
            while(count>0)
            {
                headB=headB.next;
                count--;
            }
        }
        p=headA;q=headB;
        while(p!=null&&q!=null&&p!=q)
        {
            p=p.next;
            q=q.next;
        }
        return p;
        
    }
    
}

206. 反转链表

//思路:使用头插法即可解决
//但是有一个问题,p=head,
//移动p的时候,如果改变了p.next,那么head也会变,下面的并没有改变原来节点间的关系
 while(p!=null)
        {
            count_A++;
            p=p.next;
        }
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //设置一个头节点
        ListNode pre =new ListNode(0);
        pre.next=null;
        while(head!=null)
        {
            ListNode p=head;
            head=head.next;//head 先保存好下一个节点,这样才能继续遍历
            p.next=pre.next;
            pre.next=p;
            
        }
        return pre.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值