https://leetcode-cn.com/tag/array/
题目汇总
34. 在排序数组中查找元素的第一个和最后一个位置中等
35. 搜索插入位置简单
39. 组合总和中等
40. 组合总和 II中等
41. 缺失的第一个正数困难
42. 接雨水困难
45. 跳跃游戏 II困难
48. 旋转图像中等
53. 最大子序和简单
54. 螺旋矩阵中等
34. 在排序数组中查找元素的第一个和最后一个位置中等
给定一个按照升序排列的整数数组
nums
,和一个目标值target
。找出给定目标值在数组中的开始位置和结束位置。你的算法时间复杂度必须是 O(logn) 级别。如果数组中不存在目标值,返回[-1, -1]
。
输入: nums = [5,7,7,8,8,10]
, target = 8,输出: [3,4]
思路:二分查找
二分查找算法细节详解这位博主写的简直不能太清晰!!!值得一看!!!
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = left_bound(nums, target);
int right = right_bound(nums, target);
return new int[]{left, right};
}
public int left_bound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,收缩左侧边界
right = mid - 1;
}
}
// 最后要检查 left 越界的情况
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
public int right_bound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 别返回,收缩右侧边界
left = mid + 1;
}
}
// 最后要检查 right 越界的情况
if (right < 0 || nums[right] != target)
return -1;
return right;
}
}
35. 搜索插入位置简单
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
输入: [1,3,5,6], 5,输出: 2
思路:二分查找
每次根据 nums[mid] 和 target 之间的大小进行判断,相等则直接返回下标,nums[mid] < target 则start右移,nums[mid] > target 则 end 左移
查找结束如果没有相等值则返回 start,该值为插入位置
class Solution {
public int searchInsert(int[] nums, int target) {
if(nums.length==0||nums==null)
return 0;
int start = 0;
int end = nums.length-1;
while(start<=end){
int mid = start+(end-start)/2;
if(nums[mid]==target){
return mid;
}
else if(nums[mid]<target){
start = mid + 1;
}
else{
end = mid - 1;
}
}
return start;
}
}
39. 组合总和中等
给定一个无重复元素的数组
candidates
和一个目标数target
,找出candidates
中所有可以使数字和为target
的组合。candidates
中的数字可以无限制重复被选取。
说明: 所有数字(包括target
)都是正整数,解集不能包含重复的组合。
输入: candidates =[2,3,6,7],
target =7
,所求解集为:
[
[7],
[2,2,3]
]
思路:回溯算法+剪枝
这个解答讲解清楚,点它!!!我何时才能拥有这样的脑子呢,哦我不配
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
int len = candidates.length;
Arrays.sort(candidates);
dfs(candidates, len, target, 0, new ArrayDeque<>(), res);
return res;
}
/**
* @param candidates 数组输入
* @param len 输入数组的长度,冗余变量
* @param residue 剩余数值
* @param begin 本轮搜索的起点下标
* @param path 从根结点到任意结点的路径
* @param res 结果集变量
*/
private void dfs(int[] candidates,
int len,
int residue,
int begin,
Deque<Integer> path,
List<List<Integer>> res) {
if (residue == 0) {
// 由于 path 全局只使用一份,到叶子结点的时候需要做一个拷贝
res.add(new ArrayList<>(path));
return;
}
for (int i = begin; i < len; i++) {
// 在数组有序的前提下,剪枝
if (residue - candidates[i] < 0) {
break;
}
path.addLast(candidates[i]);
dfs(candidates, len, residue - candidates[i], i, path, res);
path.removeLast();
}
}
}
40. 组合总和 II中等
给定一个数组
candidates
和一个目标数target
,找出candidates
中所有可以使数字和为target
的组合。
candidates
中的每个数字在每个组合中只能使用一次。
说明:
- 所有数字(包括目标数)都是正整数。
- 解集不能包含重复的组合。
输入: candidates =[10,1,2,7,6,1,5]
, target =8
,所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
思路:回溯法+剪枝
以 target 为根结点,依次减去数组中的数字,直到小于 0 或者等于 0,把等于 0 的结果记录到结果集中。
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
int len = candidates.length;
Arrays.sort(candidates);
dfs(candidates, len, target, 0, new ArrayDeque<>(), res);
return res;
}
private void dfs(int[] candidates,
int len,
int residue,
int begin,
Deque<Integer> path,
List<List<Integer>> res) {
if (residue == 0) {
// 由于 path 全局只使用一份,到叶子结点的时候需要做一个拷贝
res.add(new ArrayList<>(path));
return;
}
for (int i = begin; i < len; i++) {
// 在数组有序的前提下,大剪枝
if (residue - candidates[i] < 0) {
break;
}
// 小剪枝,去除重复元素
if (i > begin && candidates[i] == candidates[i - 1]) {
continue;
}
path.addLast(candidates[i]);
dfs(candidates, len, residue - candidates[i], i+1, path, res);
path.removeLast();
}
}
}
41. 缺失的第一个正数困难
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
输入: [1,2,0],输出: 3
输入: [7,8,9,11,12],输出: 1
思路:映射哈希+交换
数值为 i 的数映射到下标为 i - 1 的位置上
评论区大佬添加的注释很全面:使用座位交换法
缺失的第一个整数是 [1, len + 1] 之间,那么我们可以遍历数组,然后将对应的数据填充到对应的位置上去,比如 1 就填充到 nums[0] 的位置, 2 就填充到 nums[1],如果填充过程中, nums[i] < 1 && nums[i] > len,那么直接舍弃。填充完成,我们再遍历一次数组,如果对应的 nums[i] != i + 1,那么这个 i + 1 就是缺失的第一个正数
比如 nums = [7, 8, 9, 10, 11], len = 5
我们发现数组中的元素都无法进行填充,直接舍弃跳过,那么最终遍历数组的时候,我们发现 nums[0] != 0 + 1,即第一个缺失的是 1
比如 nums = [3, 1, 2], len = 3
填充过后,我们发现最终数组变成了 [1, 2, 3],每个元素都对应了自己的位置,那么第一个缺失的就是 len + 1 == 4
class Solution {
public int firstMissingPositive(int[] nums) {
for(int i=0;i<nums.length;i++){
while(nums[i]>0&&nums[i]<=nums.length&&nums[nums[i]-1]!=nums[i]){
swap(nums,nums[i]-1,i);
}
}
for(int i=0;i<nums.length;i++){
if(nums[i]!=i+1){
return i+1;
}
}
return nums.length+1;
}
public void swap(int[] nums, int index1, int index2){
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
42. 接雨水困难
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
输入: [0,1,0,2,1,0,1,3,2,1,2,1],输出: 6
思路一:暴力法,时间复杂度O(n*n)
对于数组中的每个元素,下雨后水能达到的最高位置,等于两边最大高度的较小值减去当前高度的值。为了找到最大值每次都要向左和向右扫描一次。
class Solution {
public int trap(int[] height) {
int sum = 0;
for(int i=1;i<height.length-1;i++){
int max_left = 0;
int max_right = 0;
//左边最高列
for(int j=i;j>=0;j--){
if(height[j]>max_left)
max_left = height[j];
}
//右边最高列
for(int j=i;j<height.length;j++){
if(height[j]>max_right)
max_right = height[j];
}
int min = Math.min(max_left,max_right);
//只有较小的一列大于当前列的高度才会有水
if (min > height[i])
sum += (min - height[i]);
}
return sum;
}
}
思路二:动态规划,时间复杂度O(n)
在按列求解的暴力法中,对于每一列,我们求它左边最高的墙和右边最高的墙,都是重新遍历一遍所有高度。为了降低时间复杂度,定义两个数组,max_left [i] 代表第 i 列左边最高的墙的高度,max_right[i] 代表第 i 列右边最高的墙的高度。
class Solution {
public int trap(int[] height) {
int sum = 0;
int[] max_left = new int[height.length];
int[] max_right = new int[height.length];
for(int i = 1; i < height.length - 1; i++){
max_left[i] = Math.max(max_left[i-1],height[i-1]);
}
for(int i = height.length - 2; i >= 0; i--){
max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
}
for (int i = 1; i < height.length - 1; i++) {
int min = Math.min(max_left[i],max_right[i]);
//只有较小的一列大于当前列的高度才会有水
if(min>height[i])
sum += (min - height[i]);
}
return sum;
}
}
45. 跳跃游戏 II困难
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。
输入: [2,3,1,1,4],输出: 2
解释: 跳到最后一个位置的最小跳跃数是2
。从下标为 0 跳到下标为 1 的位置,跳1
步,然后跳3
步到达数组的最后一个位置。
说明:假设你总是可以到达数组的最后一个位置。
思路:贪心算法
搬运解题区精选解题的顺藤摸瓜
class Solution {
public int jump(int[] nums) {
int end = 0;
int maxPosition = 0;
int steps = 0;
for(int i = 0; i < nums.length - 1; i++){
//找能跳的最远的
maxPosition = Math.max(maxPosition, nums[i] + i);
if( i == end){ //遇到边界,就更新边界,并且步数加一
end = maxPosition;
steps++;
}
}
return steps;
}
}
48. 旋转图像中等
给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。
说明:你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
思路:转置+翻转,时间复杂度O(n*n)
先转置矩阵,然后翻转每一行
class Solution {
public void rotate(int[][] matrix) {
int len = matrix.length;
//矩阵转置
for(int i=0;i<len;i++){
for(int j=0;j<i;j++){
int temp = matrix[j][i];
matrix[j][i] = matrix[i][j];
matrix[i][j] = temp;
}
}
//翻转
for(int i=0;i<len;i++){
for(int j=0;j<len/2;j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[i][len-j-1];
matrix[i][len-j-1] = temp;
}
}
}
}
53. 最大子序和简单
给定一个整数数组
nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
输入: [-2,1,-3,4,-1,2,1,-5,4],输出: 6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
思路一:动态规划
首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans
如果 sum > 0,则 sum 保留并加上当前遍历数字
如果 sum <= 0,则需要舍弃,sum 直接更新为当前遍历数字
每次比较 sum 和 ans 的大小,将最大值置为ans,遍历结束返回结果
时间复杂度O(n)
class Solution {
public int maxSubArray(int[] nums) {
int ans = nums[0];
int sum = 0;
for(int i=0;i<nums.length;i++){
if(sum>0){
sum += nums[i];
}
else{
sum = nums[i];
}
ans = Math.max(sum,ans);
}
return ans;
}
}
思路二:分冶法
54. 螺旋矩阵中等
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
与剑指Offer的顺时针打印矩阵相同
思路:设置上下左右四个界限法
定义四个变量代表范围,up、down、left、right
1.向右走存入整行的值,当存入后,该行再也不会被遍历,代表上边界的 up 加一,同时判断是否和代表下边界的 down 交错
2.向下走存入整列的值,当存入后,该列再也不会被遍历,代表右边界的 right 减一,同时判断是否和代表左边界的 left 交错
3.向左走存入整行的值,当存入后,该行再也不会被遍历,代表下边界的 down 减一,同时判断是否和代表上边界的 up 交错
4.向上走存入整列的值,当存入后,该列再也不会被遍历,代表左边界的 left 加一,同时判断是否和代表右边界的 right 交错
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> list = new ArrayList<>();
if(matrix==null||matrix.length==0||matrix[0].length==0){
return list;
}
int up = 0;//上边界
int down = matrix.length - 1;//下边界,行数-1
int left = 0;//左边界
int right = matrix[0].length - 1;//右边界,列数-1
while(true){
//最上边一行,向右走存入整行的值
for(int col=left;col<=right;col++){
list.add(matrix[up][col]);
}
up++;//向下逼近
if(up>down){
break;
}
//最右边一列,向下走存入整列的值
for(int row=up;row<=down;row++){
list.add(matrix[row][right]);
}
right--;//向左逼近
if(right<left){
break;
}
//最下边一行,向左走存入整行的值
for(int col=right;col>=left;col--){
list.add(matrix[down][col]);
}
down--;//向上逼近
if(down<up){
break;
}
//最左边一列,向上走存入整列的值
for(int row=down;row>=up;row--){
list.add(matrix[row][left]);
}
left++;//向右逼近
if(left>right){
break;
}
}
return list;
}
}
网友评论