Leetcode数组专题专练:经典题目+思路解读

简介: Leetcode数组专题专练:经典题目+思路解读

数组专题总结

12dddb6862c54f0ba6843a47804a38f8.gif

数组在内存中的存储方式,是在连续内存空间上的相同类型数据的集合,可以方便通过下标索引的方式来获取到下标下对应的数据。

需要注意的是:数组下标是从0开始,数组内存空间的地址是连续的,因为数组的内存空间的地址是连续的,所以我们在删除或者添加元素的时候,就难免要移动其他元素的地址。

如果要删除一个数组里的某个数,那么该元素后的所有元素都要做移动的操作,所以数组的元素是不能够删除的,只能够覆盖。

java中一维数组内存是连续的,但是二维数组不是连续的,二维数组的内存空间是由四条连续的地址空间组成。


二分法专练

  1. Leetcode704 二分查找

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


经典的二分模板类型题,对于已经排序好的数组,要找某一个数,直接要想到可以使用二分法来进行解决。时间复杂度O(logn)

java参考代码:

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


go参考代码:

func search(nums []int, target int) int {
    if target<nums[0] || target>nums[len(nums)-1]{
        return -1;
    }
    left,right := 0,len(nums)-1
    for left<=right{
        mid := left+((right-left)>>1)
        if nums[mid] == target{
            return mid
        }
        if nums[mid]<target{
            left =mid+1
        }
        if nums[mid]>target{
            right =mid-1
        }
    }
    return -1
}


  1. Leetcode35 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。


这道题要求使用O(logn),同时也是排序后的数组,所以可以使用二分法来进行排序

java参考代码:

class Solution {
    public int searchInsert(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{
              right = mid-1;
          }
      }
      return left;
    }
}


go参考代码:

func searchInsert(nums []int, target int) int {
    left,right :=0,len(nums)-1
    for left<= right{
        mid:=left+((right-left)>>1)
        if nums[mid] == target {
            return mid
        }
        if nums[mid] > target {
            right = mid-1
        }
        if nums[mid] < target {
            left = mid+1
        }
    }
    return left
}


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

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。


题中对时间复杂度进行了要求,同时题中要求了数组为非递减,所以可以使用二分法。但是这道题返回的是一个范围,我们知道,二分法每次只能确定一个值,所以我们可以通过两次二分,来分别获得左边界和右边界的值,最后返回一个区间。

本文之前有过讲解,具体思路和流程参考这里:Leetcode34思路解读

java参考代码:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = searchLeft(nums,target);
        int right= searchright(nums,target);
        if(left == -1 || right == -1){
            return new int[]{-1,-1};
        }else{
            return new int[]{left,right};
        }
    }
    //寻找目标值左边界m
    public int searchLeft(int[]nums,int target){
        int left=0,right=nums.length-1;
        while(left<=right){
            int mid =left+((right-left)>>1);
            if(nums[mid] == target){
                if(mid == 0 || nums[mid-1]<target){
                    return mid;
                }
                right =mid-1;
            }
            if(nums[mid]>target){
                right = mid-1;
            }
            if(nums[mid]<target){
                left = mid+1;
            }
        }
        return -1;
    }
    public int searchright(int []nums,int target){
        int left = 0,right=nums.length-1;
        while(left<=right){
            int mid = left+((right-left)>>1);
            if(nums[mid] == target){
                if(mid == nums.length-1 || nums[mid+1]>target){
                    return mid;
                }
                left = mid+1;
            }
            if(nums[mid]>target){
                right=mid-1;
            }
            if(nums[mid]<target){
                left = mid+1;
            }
        }
        return -1;
    }
}


go参考代码:

func searchRange(nums []int, target int) []int {
    left := searchleft(nums,target)
    right := searchright(nums,target)
    if left== -1 || right == -1{
        return []int{-1,-1}
    }else{
        return []int{left,right}
    }
}
func searchleft(nums [] int,target int) int{
    left,right:=0,len(nums)-1
    for left<=right{
        mid := left+((right-left)>>1)
        if nums[mid] == target{
            if mid == 0 || nums[mid-1]<target{
                return mid
            }
            right = mid-1
        }
        if nums[mid]<target{
            left =mid+1
        }
        if nums[mid]>target{
            right = mid-1
        }
    }
    return -1
}
func searchright(nums [] int,target int) int{
    left,right:=0,len(nums)-1
    for left<=right{
        mid := left+((right-left)>>1)
        if nums[mid] == target{
            if mid == len(nums)-1 || nums[mid+1]>target{
                return mid
            }
            left = mid+1
        }
        if nums[mid]<target{
            left =mid+1
        }
        if nums[mid]>target{
            right = mid-1
        }
    }
    return -1
}


通过上面两道题的解题思路,你应该能有很多收获,下面这里两道题就当做课后练习吧,不会的话可以回顾前面题解思路,尝试自己去把做出来


  1. x的平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。


  1. 有效的完全平方和

给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。

不能使用任何内置的库函数,如 sqrt 。


双指针专练

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


  1. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。


本题主要考点是双指针中的快慢指针的应用,相信通过1,2,3题,你会有很多收获。本题之前有过讲解:可以参考该文献:移除元素思路讲解


  1. 删除有效数组的重复项

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。


本题的话,也是双指针的一个应用,快慢指针,本文之前有过讲解:可供参考思路:删除有效数组的重复项思路讲解


  1. 移动零

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

请注意 ,必须在不复制数组的情况下原地对数组进行操作。


本题同样是双指针中快慢指针的练习,你可以尝试不看题解,自己做出来,就当做前两道题的练习题,考验一下自己的知识的了解程度

本文之前有过讲解:可以参考文章思路:移动零思路讲解


  1. 比较含退格的字符串
    这道题可以使用栈和双指针进行解答,难度感觉还是不简单,可以参考我之前文章思路比较含退格的字符串思路讲解

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true

。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。


  1. 有序数组的平方
    本题同样可以使用双指针解题,分为首尾双指针法,不是前几道题的快慢双指针,题解思路可以参考之前的文献:有序数组的平方思路讲解


滑动窗口专练

  1. 长度最小的子数组

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

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

可以先思考,没有思路和头绪的话可以参考我之前的文章:长度最小的子数组思路解读

下面给出两道类似的滑动窗口练习题:做题首先想思路,没有思路的话可以参考题解


  1. 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。

你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。

一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。


  1. 最小覆盖子串

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

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

如果 s 中存在这样的子串,我们保证它是唯一的答案。


模拟行为专练

  1. 螺旋矩阵

题目描述

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。


示例 1:

79347807ef704936a44b5870acc35a10.png

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]


示例 2:

461dda2c3b3a48d289fe40619ba5744b.png

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]


可以先思考,同时可以参考我之前文献思路:螺旋数组思路解读


  1. 螺旋数组II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例 1:

e53c94c86a484231bd4b38bc40915c03.png

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]


示例 2:

输入:n = 1
输出:[[1]]

可以先思考,同时可以参考我之前的文献:螺旋数组II思路解读


  1. 剑指 Offer 29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。


示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]


示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]


go参考代码:

func spiralOrder(matrix [][]int) []int {
    if(len(matrix) == 0){
        return []int{}
    }
    top,left,right,bottom :=0,0,len(matrix[0])-1,len(matrix)-1
    res :=[]int{}
    for left<=right && top <=bottom{
        for i:=left;i<=right;i++{res=append(res,matrix[top][i])}
        top++
        if top>bottom{
            return res
        }
        for i:=top;i<=bottom;i++{res=append(res,matrix[i][right])}
        right--
        if right<left{
            return res
        }
        for i:=right;i>=left;i--{res=append(res,matrix[bottom][i])}
        bottom--
        if bottom<top{
            return res
        }
        for i:=bottom;i>=top;i--{res=append(res,matrix[i][left])}
        left++
        if left>right{
            return res
        }
    }
    return res
}

希望对您的算法学习能有所帮助,您的支持是我前进的最大动力,感谢您的关注和认可!!!

相关文章
|
1月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
38 0
|
3月前
|
算法
LeetCode第53题最大子数组和
LeetCode第53题"最大子数组和"的解题方法,利用动态规划思想,通过一次遍历数组,维护到当前元素为止的最大子数组和,有效避免了复杂度更高的暴力解法。
LeetCode第53题最大子数组和
|
3月前
|
存储 Java API
LeetCode------合并两个有序数组(4)【数组】
这篇文章介绍了LeetCode上的"合并两个有序数组"问题,并提供了三种解法:第一种是使用Java的Arrays.sort()方法直接对合并后的数组进行排序;第二种是使用辅助数组和双指针技术进行合并;第三种则是从后向前的双指针方法,避免了使用额外的辅助数组。
LeetCode------合并两个有序数组(4)【数组】
LeetCode------找到所有数组中消失的数字(6)【数组】
这篇文章介绍了LeetCode上的"找到所有数组中消失的数字"问题,提供了一种解法,通过两次遍历来找出所有未在数组中出现的数字:第一次遍历将数组中的每个数字对应位置的值增加数组长度,第二次遍历找出所有未被增加的数字,即缺失的数字。
|
3月前
|
前端开发
LeetCode------移动零(5)【数组】
这篇文章介绍了LeetCode上的"移动零"问题,提出了一种使用双指针的原地操作解法,该方法首先将非零元素移动到数组前端并保持相对顺序,然后填充后续位置为零,以达到题目要求。
|
1月前
|
程序员 C语言
【C语言】LeetCode(力扣)上经典题目
【C语言】LeetCode(力扣)上经典题目
|
1月前
【LeetCode-每日一题】 删除排序数组中的重复项
【LeetCode-每日一题】 删除排序数组中的重复项
19 4
|
1月前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
18 0
Leetcode第三十三题(搜索旋转排序数组)
|
1月前
|
算法 C++
Leetcode第53题(最大子数组和)
这篇文章介绍了LeetCode第53题“最大子数组和”的动态规划解法,提供了详细的状态转移方程和C++代码实现,并讨论了其他算法如贪心、分治、改进动态规划和分块累计法。
56 0
|
1月前
|
C++
【LeetCode 12】349.两个数组的交集
【LeetCode 12】349.两个数组的交集
16 0