leetcode数组刷题总结

理论基础

数组是存放在连续内存空间上的相同类型数据的集合。

数组下标都是从0开始的。
数组内存空间的地址是连续的

704.二分查找

在这里插入图片描述
大家写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
(左闭右闭)

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

二分查找系列题目

重要理论 涉及到二段性的问题

给结论
详细过程见此
在这里插入图片描述
在这里插入图片描述

华为机考:交易系统的阈值 (二段性查找问题) *****

在这里插入图片描述
对于边界值的选取 每次对mid left right的更新,如何保证不进入死循环

public class Solution1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        String[] parts = line.split(" ");
        int size = parts.length;
        int[] nums = new int[size];
        for(int i=0;i<size;i++){
            nums[i] = Integer.parseInt(parts[i]);
        }
        int target = sc.nextInt();

        //左闭右开区间
        int left = 0;
        int right = (int)1e5;
       // int right  = 15;
        while(left<right){
            int mid = (left+right+1)>>1;
//                int mid = (left+right)>>1;
            if(check(nums,mid,target)){
                left = mid ;
            }else{
                right = mid-1;
            }
        }
        System.out.println(left);


    }
    //是否满足要求
    public static boolean check(int[] nums,int limit,int target){
        int sum = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]<=limit){
                sum += nums[i];
            }else{
                sum += limit;
            }
        }
        if(sum>target){
            return false;
        }else{
            return true;
        }
    }
}
153. 寻找旋转排序数组中的最小值

在这里插入图片描述

class Solution {
   //二分查找寻找数组的最小值
   public int findMin(int[] nums) {
       int left = 0;
       int right = nums.length - 1;
       while(left<right){
           int mid = (left + right)/2;
           if(nums[mid]>nums[right]){
               left = mid + 1;
           }else{
               right = mid;
           }
       }
       return nums[right];
   }
}
33. 搜索旋转排序数组 *****

在这里插入图片描述

class Solution {
    public 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[0]<=nums[mid]){//左边有序
                if(nums[0]<=target && nums[mid]>target){//左闭右闭,左区间必须取等
                    right = mid - 1;

                }else{
                    left = mid + 1;  
                }
            }else{
                if(nums[mid]<target && target<=nums[right]){//同理 左闭右闭
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
}
278. 第一个错误的版本

在这里插入图片描述
防止溢出:使用减法

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left = 1;
        int right = n;
        while(left<right){
            int mid = left+(-left+right)/2;
            if(isBadVersion(mid)){
                right = mid;
            }else{
                left = mid + 1;
            }
        }
        return left;
        
    }
}
35.搜索插入位置
34.在排序数组中查找元素的第一个和最后一个位置
69.x的平方根
367.有效的完全平方数

27.移除元素 (双指针)

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        for (int fast = 0; fast < nums.length; fast++){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow += 1;
            }
        }
        return slow;
//双指针遍历到非val的值 slow+1
    }
}

相关题目推荐

26.删除排序数组中的重复项

在这里插入图片描述
双指针,慢指针指向新数组的元素,快指针用来遍历。只有当快慢指针不同时,进行元素复制。相同的话就进行指针的移动

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length == 1){
            return 1;
        }
        int slow = 0;
        int fast = 1;
        while(fast<nums.length){
            if(nums[slow] == nums[fast]){
                fast++;
            }else{
                nums[slow+1] = nums[fast];
                slow++;
                fast++;
            }
        }
        return slow+1;
    }
}
283.移动零
844.比较含退格的字符串
977.有序数组的平方

209.长度最小的子数组(最小滑动窗口)

在这里插入图片描述
窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?

  1. 窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
  2. 窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
  3. 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int result = Integer.MAX_VALUE;//定义无穷大
        int left = 0;
        int right = 0;
        int sum = 0;
        while (right<nums.length){
            sum = sum + nums[right];
            while (sum>=target){
                result = Math.min(result,right-left+1);
                sum = sum - nums[left];
                left++;
            }
            right++;
        }
        if (result==Integer.MAX_VALUE){return 0;}
        return result;
    }
}

滑动窗口模板总结

最小滑动窗口

while j < nums.length:
    判断[i, j]是否满足条件
    while 满足条件:
        不断更新结果(注意在while内更新!)
        i += 1 (最大程度的压缩i,使得滑窗尽可能的小)
    j += 1

最大滑动窗口

while j < nums.length:
    判断[i, j]是否满足条件
    while 不满足条件:
        i += 1 (最保守的压缩i,一旦满足条件了就退出压缩i的过程,使得滑窗尽可能的大)
    不断更新结果(注意在while外更新!)
    j += 1

区别

最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果

相关题目

904.水果成篮(最大滑动窗口)
class Solution {
    //哈希表+最大滑动窗口
    public int totalFruit(int[] fruits) {
        int left = 0;
        int right = 0;//左右边界
        int result = 0;//最终结果
        int count = 0;//水果类别个数
        Map<Integer,Integer> map = new HashMap<>();//哈希表为 水果种类:个数
        for (int i=0;i<fruits.length;i++){
            map.put(fruits[i],0);
        }
        while(right<fruits.length){
            //1. 判断是否有当前水果(不存在的话类别加1) 其放入果篮当中 更新map
            if (map.get(fruits[right])==0){
                count++;//没有当前水果,种类要加1
            }
            map.put(fruits[right],map.get(fruits[right])+1);//放入果篮 key:水果类别,value:在原来的个数基础上加1
            // 2. 看当前水果种类是否大于2
            while (count>2){
                //判断第一种类别的水果个数是否为1(等于1的话 类别减去1) 更新map
                if (map.get(fruits[left])==1){
                    count--;
                }
                map.put(fruits[left],map.get(fruits[left])-1);
                left++;//左边界左移
            }
            result = Math.max(result,right-left+1);
            right++;
        }
        return result;
    }
}
76. 最小覆盖子串

在这里插入图片描述

class Solution {
    //最小滑窗
    public String minWindow(String s, String t) {
        if(s.length() == 0 || s == null || t.length() == 0 || t == null){
            return "";//特殊情况
        }
        int left = 0;
        int right = 0;
        int start = 0;//记录最终结果
        int size = Integer.MAX_VALUE;//记录字符串的大小
        int count = t.length();//需要的数
        int[] need = new int[128];//记录需要的字符个数
        for(int i=0;i<t.length();i++){
            need[t.charAt(i)]++;
        }

        while(right<s.length()){
            char c = s.charAt(right);//取字符
            if(need[c]>0){//需要这个字符
                count--;
            }
            need[c]--;//入窗口
            //当count==0,进行处理
            if(count == 0){
                while(left<right && need[s.charAt(left)]<0){//need元素小于0 说明他不是t所需要的
                    need[s.charAt(left)]++;
                    left++;
                } 
                //记录最大值
                if(right-left+1<size){
                    size = right - left + 1;
                    start = left;
                }
                //need>=0的情况了
                need[s.charAt(left)]++;
                left++;
                count++;//与前面区别
            }
            //更新右边界
            right++;
        }
        return size == Integer.MAX_VALUE ? "" : s.substring(start,start+size);

    }
}
1004. 最大连续1的个数 III
3. 无重复字符的最长子串

59.螺旋矩阵Ⅱ(模拟题目,考察代码能力)

在这里插入图片描述
模拟画圆圈,注意区间统一就好

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] result = new int[n][n];
        int loop = 0;//循环次数
        int i,j; //遍历的坐标
        int start = 0;//开始坐标 x和y相同
        int count = 1;

//1.循环画圈
        //边界全部采取左闭右开
        while (loop < n/2){
            //1.1 从左到右
            for (j = start;j < n-loop-1;j++){
                result[start][j] = count;
                count++;
            }
            // 1.2 从上到下
            for (i = start;i < n - loop-1;i++){
                result[i][j] = count;
                count++;
            }
            // 1.3 从右到左
            for (;j>loop;j--){
                result[i][j] = count;
                count++;
            }
            for(;i>loop;i--){
                result[i][j] = count;
                count++;
            }
            start++;
            loop++;
        }
//2.奇数个中间有点     
        if (n % 2 == 1){
            result[start][start] = count;
        }
        return result;
    }
}

相关题目

54.螺旋矩阵
剑指Offer 29.顺时针打印矩阵
LeetCode 是一个在线编程学习平台,提供大量编程题目供用户练习,特别适合准备技术面试的开发者。在 Java 方面,LeetCode 提供了各种难度的练习题,从基础的数组、字符串操作,到复杂的图和动态规划问题。下面是一份简单的 Java 刷题指南: 1. **熟悉 Java 基础**:在开始刷题之前,确保你对 Java 语言有扎实的理解,包括数据类型、数组、集合、异常处理、流、lambda 表达式等。 2. **理解题目要求**:仔细阅读题目描述,理解输入输出格式和限制条件,如时间复杂度和空间复杂度的限制。 3. **编写测试用例**:在编写代码之前,先编写一些测试用例来验证你的想法是否正确。LeetCode 平台本身提供了测试用例,但自己编写测试用例有助于加深对问题的理解。 4. **编写代码**:开始编写代码时,尽量使代码简洁易懂。注意代码风格和命名规范,以便于他人(或未来的你)阅读和维护。 5. **调试和优化**:编写完代码后,通过 LeetCode 提供的测试用例进行调试。如果失败,检查逻辑错误并优化代码。注意时间复杂度和空间复杂度,尝试找到更优解。 6. **学习算法和数据结构**:很多 LeetCode 题目都需要使用特定的算法和数据结构。通过刷题,你可以学习和巩固这些知识,例如排序算法、树、图、动态规划等。 7. **参与讨论和查看解法**:如果在解决问题时遇到困难,可以查看 LeetCode 上其他人的解法和讨论,这有助于开阔思路,学习新的解题技巧。 8. **定期回顾**:定期回顾已经解决的问题,以加深记忆和巩固知识。 9. **参加 LeetCode 比赛**:参加 LeetCode 定期举办的线上编程竞赛,可以锻炼你在压力下的编程能力和解题速度。 10. **制定计划和目标**:为自己制定一个学习计划,设定短期和长期目标。例如,每天解决一题或者每周掌握一种新的算法
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值