LeetCode数组(二分查找,双指针,滑动窗口,区间问题,二维数组)

文章目录

  • 前言
  • 题目&推荐列表
  • 二分查找
    • 1. lc704 二分查找
    • 2. lc34 在排序数组中查找元素的首和末位置
  • 双指针&滑动窗口
    • 1. lc27 移除元素
    • 2. lc283 移动零
    • 3. lc209 长度最小的子数组
  • 前缀和数组
    • 1. lc303 区域和检索——数组不可变
    • 2. lc304 二维区域和检索 - 矩阵不可变
  • 区间问题
    • 1. lc56 合并区间
    • 2. lc57 插入区间
    • 3. lc1288 删除被覆盖区间
  • 模拟&其他
    • 1.lc941 有效的山脉数组
  • 二维数组
    • 1.lc54.螺旋矩阵

前言

数组可以说是大家最早接触的数据结构了,上手应该比较快。如果对数组还有不熟悉的可以看这篇:数组的基本操作,可以快速回顾数组查找,删除,添加以及内存分布等

本文主要是leetcode的习题解析。数组方面的算法非常多,一定要多思考思考:二分查找,双指针,滑动窗口,哈希,前缀和,模拟……
常见的技巧有:

  • for循环暴力:相信拿到数组80%题目的第一反应都是暴搜(太香了)
  • 二分查找:“有序数组,无重复元素”——二分查找
  • 双指针:通过两个指针(如快慢指针)在一个for循环下完成两个for循环的工作。
  • 滑动窗口:不断的调节子序列的起始位置和终止位置

题目&推荐列表

本文涉及的题目汇总:

题号 标签/分类 难度
1. lc35 搜索插入位置 二分法 easy
2. lc704 二分查找 二分法 easy
3. lc69 x的平方根 二分法 easy
4. lc327 移除元素 双指针 easy
5. lc283 移动零 双指针 easy
6. lc209 长度最小的子数组 滑动窗口 medium
7. lc1207 独一无二的出现次数 哈希 easy
8. lc1365 有多少小于当前数字的数字 哈希 easy
9. lc941 有效的山脉数组 其他 easy
10. lc724 寻找数组的中心下标 其他 easy
11. lc54 螺旋矩阵 二维数组 easy

推荐:

题号 标签/分类 难度
lc367 有效的完全平方数 二分法 easy
lc977 有序数组的平方 双指针 / 排序 easy
lc1365 有多少小于当前数字的数字 其他 easy
lc34 在排序数组中找到元素的首位置和末位置 二分法 medium

二分查找

这里只列举最基础的二分查找,有关更详细更复杂的二分查找可以参见:

1. lc704 二分查找

力扣704 链接

描述:

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例:

输入: nums = [-1,0,3,5,9,12], target = 9  输出: 4

solution:
标准的二分查找,可以把下面的代码看成一个模板。注意两个地方
left<=right,left = mid+1,right=mid-1

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

2. lc34 在排序数组中查找元素的首和末位置

力扣34链接

描述:

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。

示例:

输入:nums = [5,7,7,8,8,10], target = 8   输出:[3,4]

Solution:
二分查找升级版! 两次二分查找~

在这里插入代码片

双指针&滑动窗口

可以说除了暴力的方法,像双指针还有滑动窗口,都是为了减少for循环的层数,因为很多问题两层for循环算法复杂度直接O(n^2),所以双指针就是为了在一个for循环中完成两个for循环的事,滑动窗口也一样。

双指针不算是一种数据结构,但是这个技巧可谓贯穿很多题目,比如数组,链表,字符串……在 双指针详解 中有对双指针详细的介绍,本文主要是针对数组的。

而滑动窗口,其实也逃不开双指针,他更关心两个指针之间的内容(所以像一个窗口,当两个指针在移动时,其实就是窗口在滑动),有关滑动窗口的更多,可参见:力扣滑动窗口详解

1. lc27 移除元素

力扣27链接

描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

示例:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]

Solution:

第一眼暴力两层for循环~(时间复杂度O(n^2)),这里就不贴代码了。

快慢双指针
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作

快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置

 public int removeElement(int[] nums, int val) {
        // 定义快慢指针
        int slow = 0;
        int fast = 0;

        for(fast=0;fast<nums.length;fast++)
        {
            int num = nums[fast];
            if(num != val)
            {
                nums[slow] = num;
                slow++;
            } 
        }
        return slow;
    }

2. lc283 移动零

力扣283链接

描述:

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: nums = [0,1,0,3,12] 输出: [1,3,12,0,0]

solution:

看上去和第一题很相似
注意你在操作数组的时候数组里面的元素都在变

Solution1:
言归正传,模仿第一题的解法,双指针,然后后面添0

public static void moveZeroes(int[] nums) {
       int low=0;
       int fast;

       int len = nums.length;

       for(fast=0;fast<len;fast++)
       {
           int num = nums[fast];
           if(num == 0)
           {
               nums[low]=num;
               low++;
           }
       }
       for(int i=low;i<len;i++)
           nums[i]=0;
 }

Solution2:
同样是双指针,但是利用交换的思想(更地道——更贴近题意的“移动零”)

public void moveZeroes(int[] nums) {
        int left = 0, right=0;

        while(right<nums.length)
        {
            if(nums[right]!=0)
            {
               // 交换
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
               
                left++;
            }
            right++;
        }
    }

3. lc209 长度最小的子数组

力扣209链接

描述:

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr],并返回其长度。如果不存在符合条件的子数组,返回 0

示例:

输入:target = 7, nums = [2,3,1,2,4,3]  输出:2

solution:
滑动窗口

 public int minSubArrayLen(int target, int[] nums) {
        int slow = 0 , right = 0;
        int sum=0;
        int min = Integer.MAX_VALUE;

        for(right=0;right<nums.length;right++)
        {
            sum += nums[right];
            while(sum>=target && slow<nums.length)
            {
                int tmpLen = right-slow+1;
                if(tmpLen<= min)
                min = tmpLen;

                sum = sum-nums[slow];
                slow++;
            }
        }
        return min==Integer.MAX_VALUE?0:min;
    }

前缀和数组

前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和

1. lc303 区域和检索——数组不可变

lc303链接

描述:

给定一个整数数组 nums,处理以下类型的多个查询:
计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和

Solution:
经典的一维前缀和

class NumArray {
    // 前缀和数组
    private int[] preSum;

    /* 输入一个数组,构造前缀和 */
    public NumArray(int[] nums) {
        // preSum[0] = 0,便于计算累加和
        preSum = new int[nums.length + 1];
        // 计算 nums 的累加和
        for (int i = 1; i < preSum.length; i++) {
            preSum[i] = preSum[i - 1] + nums[i - 1];
        }
    }
    
    /* 查询闭区间 [left, right] 的累加和 */
    public int sumRange(int left, int right) {
        return preSum[right + 1] - preSum[left];
    }
}

另外还有力扣724 寻找数组的中心下标 也可以用前缀和

2. lc304 二维区域和检索 - 矩阵不可变

lc304 链接

描述:

给定一个二维矩阵 matrix,以下类型的多个请求:
计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。实现 NumMatrix 类

Solution:
经典的二维前缀和,计算指定矩形中的和

class NumMatrix {

    int[][] preNum;
    public NumMatrix(int[][] matrix) {
        int r = matrix.length;
        int c = matrix[0].length;
        preNum = new int[r+1][c+1];

        for(int i=1;i<r+1;i++)
        {
            for(int j=1;j<c+1;j++)
            {
                preNum[i][j] = preNum[i-1][j] + preNum[i][j-1] + matrix[i - 1][j - 1] - preNum[i-1][j-1];
            }
        }
    }
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return preNum[row2+1][col2+1]-preNum[row2+1][col1]-preNum[row1][col2+1]
                     +preNum[row1][col1];  

    }
}

区间问题

区间问题,就是线段问题,让你合并所有线段、找出线段的交集。两个技巧,画图+排序!

1. lc56 合并区间

lc56链接

描述:

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组

示例:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]

Solution:

public int[][] merge(int[][] intervals) {
         // 先按照每个数组的左节点进行排序
         Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
         int n = intervals.length;
         int[][] res = new int[n][2];
         int index=-1;
         
         for(int i=0;i<n;i++)
         {
            if(index==-1|| res[index][1]<intervals[i][0])
             {
                 res[++index] = intervals[i];
             }
             else{
                 res[index][1] = Math.max(res[index][1],intervals[i][1]);
             }  
         }
         return Arrays.copyOf(res,index+1);

    }

2. lc57 插入区间

lc57链接

描述:

给你一个 无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠

示例:

输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]

Solution:
与上一题(lc56)相似,并且一种直观的思路就是把新区间插入老区间,再排序,然后就用上一题代码。但是本题已经排好序了,利用这一条件,只需把新区间插入即可。
所以分成三段处理,老区间靠左的和靠右的可以直接加入结果数组中,中间的可能与新数组重叠的需要处理一下左右区间点。

在这里插入代码片

3. lc1288 删除被覆盖区间

力扣1288链接

描述:

给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。

示例:

输入:intervals = [[1,4],[3,6],[2,8]] 输出:2

在这里插入代码片

模拟&其他

模拟或者是没有其他明确标签的题目,模拟也是一种重要的思想!

1.lc941 有效的山脉数组

力扣941链接

描述:

给定一个整数数组 arr,如果它是有效的山脉数组就返回 true,否则返回 false
山脉数组:在 0 < i < arr.length - 1 条件下,存在 i 使得:arr[0] < arr[1] < … arr[i-1] < arr[i];arr[i] > arr[i+1] > … > arr[arr.length - 1]

示例:

输入:arr = [0,3,2,1] 输出:true
输入:arr = [3,5,5] 输出:false

solution:
线性扫描,模拟法

 public boolean validMountainArray(int[] arr) {
        int left = 0;
        int len = arr.length;
        int right = len-1;

        while(left+1<len && arr[left]<arr[left+1] )
        left++;
        
        while(right>0 && arr[right-1]>arr[right]  )
        right--;

        if(left==right && left!=0 && right!=len)
        return true;

        return false;
    }

二维数组

(待定)

1.lc54.螺旋矩阵

力扣54链接

参考链接:
https://programmercarl.com/
https://leetcode.cn/problems/

你可能感兴趣的:(LeetCode刷题,leetcode,哈希算法,算法,数组,java)