数组类题目专题训练 -- LeetCode上7道与数组相关的题

本文通过解析7道LeetCode题目,涵盖二分查找、移除元素、平方数组、子数组和、旋转矩阵及三数/四数之和,详解数组操作技巧,提升对数组理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个专题中的题目是我跟随代码随想录的刷题计划,在LeetCode上做的与数组相关的题目,用于加深对数组的理解!

下面的内容将会有每一道题目的题意、在代码随想录中对应的参考文章、我的思路以及我所写的Java代码,希望对你有帮助!


1 - LeetCode 704 二分查找 – 二分查找法

​题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search

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

 
示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
 
示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

 
提示:

你可以假设 nums 中的所有元素是不重复的。 n 将在 [1, 10000]之间。 nums 的每个元素都将在 [-9999, 9999]之间。

参考文章

思路:

简单的二分题,主要注意点在于区分 while ( left < right ) 与 while ( left <= right ) 写法上的不同。

while ( left < right ) 对应的是左闭右开的区间的写法,while ( left <= right ) 对应的是左闭右闭的区间的写法,导致的结果是当 nums[middle] > target 时right的取值的不同:

当写 while ( left < right ) 时,注意到此时是左闭右开区间,那么right位置的值是不会考虑到的,因此当 nums[middle] > target 时,既然middle位置上的值比target大,就无须考虑middle这个位置上的值了,更新 right 值时应该是 right = middle

当写 while ( left <= right ) 时,注意到此时是左闭右闭区间,那么right位置的值是会考虑到的,因此当 nums[middle] > target 时,既然middle位置上的值比target大,同样无须考虑middle这个位置上的值了,更新 right 值时应该是 right = middle - 1

详细内容请见参考文章

本题Java代码(使用的是写法二):

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


 
 

2 - LeetCode 27 移除元素 – 双指针法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-element

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

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

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

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
  print(nums[i]);
}
 
示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
 
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
 
提示:

0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100

参考文章

思路:

主要是使用到双指针法。双指针法在数组和链表的操作中很常见,在本题中使用方法主要是设置一个快指针和一个慢指针。

下面这一图很形象地描绘出本题中快慢指针的用途(图片引用自代码随想录)

本题代码:

class Solution {
    public int removeElement(int[] nums, int val) {
        int fast = 0, slow = 0;
        while (fast < nums.length) {
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

 
 

3 - LeetCode 977 有序数组的平方 – 双指针法

​来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
 
示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
 
示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
 
提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104 nums 已按 非递减顺序 排序
 
进阶:

请你设计时间复杂度为 O(n) 的算法解决本问题

参考文章

思路:

除了使用暴力解法之外,本题的进阶写法可以考虑使用双指针法实现复杂度为O(N)的解法。

由于题目预先就提供非递减的数组,所以我们可以考虑在数组的头和尾各设置一个指针,通过比较两个指针所指的值的绝对值大小,将绝对值较大的值的平方值加入到结果数组中。

本题代码:

class Solution {
    public int[] sortedSquares(int[] nums) {
        int left = 0, right = nums.length - 1;
        int[] ans = new int[nums.length];
        int index = nums.length - 1;
        while(left<=right){
            if(Math.abs(nums[left])>Math.abs(nums[right])){
                ans[index--]=nums[left]*nums[left];
                left++;
            }else{
                ans[index--]=nums[right]*nums[right];
                right--;
            }
        }
        return ans;
    }
}

​ 
 

4 - LeetCode 209 长度最小的子数组 – 滑动窗口法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum

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

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

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
 
示例 2:

输入:target = 4, nums = [1,4,4]
输出:1
 
示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
 
提示:

1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

参考文章

思路:

使用滑动窗口的方法。滑动窗口与双指针法有点类似,使用前指针与后指针,前指针与后指针中间的部分即为滑动窗口。

以下图片很形象地说明了本题中滑动窗口的作用(图片转载自代码随想录)

本题Java代码:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int sum = 0, ans = 122222, back = 0;
        for (int fast = 0; fast < nums.length; fast++) {
            sum += nums[fast];
            while (sum >= target) {
                int tmp = fast - back + 1;
                ans = ans <= tmp ? ans : tmp;
                sum -= nums[back++];
            }
        }
        if (ans == 122222) ans = 0;
        return ans;
    }
}

 
 

5 - LeetCode 59 旋转矩阵II – 模拟


来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix-ii

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

示例 1:

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

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

1 <= n <= 20

参考文章

思路:

本题是模拟题,突破点在于将每一步操作的区域视为一个左闭右开的区间,如下图

这样基本写起来就没什么问题。设当前圈的起始位置为(x,x),每转一圈后,新的一圈的起始位置为(x+1, x+1)。

本题Java代码:

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] ans = new int[n][n];
        int start = 0;//起始位置
        int loop = n / 2;//绕圈次数
        int count = 1;//计数
        while (loop-- != 0) {
            //从左到右
            for (int i = start; i < n - 1 - start; i++) {
                ans[start][i] = count++;
            }
            //从上到下
            for (int i = start; i < n - 1 - start; i++) {
                ans[i][n - 1 - start] = count++;
            }
            //从右到左
            for (int i = n - 1 - start; i > start; i--) {
                ans[n - 1 - start][i] = count++;
            }
            //从下到上
            for (int i = n - 1 - start; i > start; i--) {
                ans[i][start] = count++;
            }
            start++;
        }
        if (n % 2 == 1) ans[start][start] = count;//当n为奇数时注意对中间的数字进行赋值
        return ans;
    }
}


​ 
 

6 - LeetCode 15 三数之和 – 双指针法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

 
示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
 
示例 2:

输入:nums = []
输出:[]
 
示例 3:

输入:nums = [0]
输出:[]
 
提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

参考文章

思路:

这道题如果考虑使用哈希法来做的话,去重是比较麻烦的,因为题目要求不可以包含重复的三元组,用哈希法的话,一个是去重过程比较繁琐,还有一个是做法很容易超时。哈希法在参考文章中有写。

这道题可以考虑使用双指针法来做,就变得简单很多。
以下这个图很形象地说明了双指针法的解法是怎么进行的(图片转自代码随想录):
在这里插入图片描述
先将数组排序,然后按顺序对于每一个值,在它之后的元素的头尾各设一个指针,然后将三个指针指向的值相加,如果和小于0,则左指针向右移动;如果和大于0,则右指针向左移动;如果和等于0,则将三个值存入结果数组,然后左右指针同时向中间移动。

同时还要注意去重问题,当下一个指针指向的值与之前指向的值相同时,就要注意应该跳过这个值。

本题Java代码:

class Solution {
	public List<List<Integer>> threeSum(int[] nums) {
		Arrays.sort(nums);
		List<List<Integer>> ans = new ArrayList<>();
		for (int i = 0; i < nums.length; i++) {
			if (nums[i] > 0)
				return ans;
			if (i > 0 && nums[i] == nums[i - 1])
				continue;
			int left = i + 1;
			int right = nums.length - 1;
			while (right > left) {
				int sum = nums[i] + nums[left] + nums[right];
				if (sum == 0) {
					ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
					while (right > left && nums[left] == nums[left + 1])
						left++;
					while (right > left && nums[right] == nums[right - 1])
						right--;
					left++;
					right--;
				} else if (sum < 0) {
					left++;
				} else {
					right--;
				}
			}
		}
		return ans;
	}
}

 
 

7 - LeetCode 18 四数之和 – 双指针法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • a、b、c 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

 
你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
 
示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
 
提示:

1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109

参考文章

思路:

这道题与《三数之和》类似,都是使用双指针来解,比使用哈希法来的更高效和简单,可以看做是《三数之和》的升级版,做这道题之前可以先把《三数之和》做一下。

这道题与《三数之和》一样需要注意去重问题。与《三数之和》的不同点在于,它使用两个循环遍历数组中每一对数字组合的和来作为《三数之和》中的第一个数,然后剩下的两个数就一样是使用双指针来操作。

另一个不同点在于《三数之和》中判断第一个数如果 num[i] > 0 就直接返回答案,但是这道题不可以说第一、二个数 num[i] > target 或 nums[j] > target 就直接返回答案,为什么呢?因为在《三数之和》中和是0,是固定的,而在这道题中target是不固定的,target有可能是负数,如果target是负数,那么有可能它是由多个大于它的数相加得到的,所以不能进行 num[i] > target 的判断就直接返回答案。

本题Java代码:

class Solution {
	public List<List<Integer>> fourSum(int[] nums, int target) {
		Arrays.sort(nums);
		List<List<Integer>> ans = new ArrayList<>();
		for (int i = 0; i < nums.length; i++) {
			if (i > 0 && nums[i] == nums[i - 1])
				continue;
			for (int j = i + 1; j < nums.length; j++) {
				if (j > i + 1 && nums[j] == nums[j - 1])
					continue;
				int left = j + 1;
				int right = nums.length - 1;
				while (right > left) {
					int sum = nums[i] + nums[j] + nums[left] + nums[right];
					if (sum == target) {
						ans.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
						while (right > left && nums[left] == nums[left + 1])
							left++;
						while (right > left && nums[right] == nums[right - 1])
							right--;
						left++;
						right--;
					} else if (sum < target) {
						left++;
					} else if (sum > target) {
						right--;
					}
				}
			}
		}
		return ans;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值