算法题解(数组篇)

本文详细介绍了二分查找的原理和实现,包括左边界和右边界查找,并给出了相关问题的解决方案,如移除元素、有序数组的平方、长度最小的子数组和螺旋矩阵的生成。这些算法均利用了数组的基础特性和二分查找的效率,展示了在有限空间内高效操作数组的方法。

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

二分查找模板

l 指针掌管左边蓝色区域, r 指针掌管右边红色区域,两者互不冲突,通过不断向目标元素靠近扩大掌管区域,直到两者掌管区域接壤。

二分查找起始状态:
在这里插入图片描述
二分查找终止状态:
在这里插入图片描述
< 、≤ 、≥ 、> 目标元素对应的蓝红区域划分
在这里插入图片描述
总结模板:
在这里插入图片描述
例子:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        //左边界
        int leftIdx = binarySearchLeft(nums, target); 
        //右边界
        int rightIdx = binarySearchRight(nums, target); 
        //判断是否符合
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) { 
            return new int[]{leftIdx, rightIdx};
        }
        return new int[]{-1, -1};
    }
    
    //找左边界的二分查找
    public int binarySearchLeft(int[] nums, int target) {
        //注意左右指针的初始值
        int left = -1, right = nums.length;
        while (left+1 != right) {
            int mid = (left + right) / 2;
            if (nums[mid] >= target) {
                right = mid;
            } else {
                left = mid;
            }
        }
        return right;
    }
     //找右边界的二分查找
    public int binarySearchRight(int[] nums, int target) {
        //注意左右指针的初始值
        int left = -1, right = nums.length;
        while (left+1 != right) {
            int mid = (left + right) / 2;
            if (nums[mid] <= target) {
                left = mid;
            } else {
                right = mid;
            }
        }
        return left;
    }

}

数组基础

数组是存放在连续内存空间上的相同类型数据的集合。
在这里插入图片描述
需要两点注意的是

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

正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。

数组的元素是不能删的,只能覆盖。

在这里插入图片描述


704. 二分查找 - 简单 - 10/13

704. 二分查找 - 简单

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

在这里插入图片描述

class Solution {
    public int search(int[] nums, int target) {
        //注意左右指针的初始值
        int left = -1, right = nums.length;
        while (left+1 != right) {
            int mid = (left + right) / 2;
            if (nums[mid] < target) {
                left = mid;
            } else {
                right = mid;
            }
        }
        //判断是否符合
        if (left < right && right < nums.length && nums[right] == target) { 
            return right;
        }
        return -1;
    }
}

在这里插入图片描述

27. 移除元素 - 简单 - 10/13

27. 移除元素 - 简单

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

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

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

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


解析:

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

class Solution {
    public int removeElement(int[] nums, int val) {
        int slowIndex = 0; //慢指针
        for(int fastIndex=0; fastIndex<nums.length; fastIndex++){
            //如果还没找到val值,就快慢指针一起移动;
            if(nums[fastIndex] != val){
                nums[slowIndex] = nums[fastIndex];
                ++slowIndex;
            }
        }
        return slowIndex;

    }
}

在这里插入图片描述

977. 有序数组的平方 - 简单 - 10/13

977. 有序数组的平方 - 简单

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

在这里插入图片描述


解析:

方法一:暴力排序,每个数平方之后,再排个序即可。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int len = nums.length; //数组长度
        int[] result = new int[len]; //新数组
        for(int i=0; i<len; i++){
            result[i] = nums[i] * nums[i]; //每个数的平方
        }
        Arrays.sort(result); //排序
        return result;
    }
}

在这里插入图片描述


方法二:双指针

数组其实是有序的, 只不过负数平方之后可能成为最大数了。

那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

此时可以考虑双指针法了,i指向起始位置,j指向终止位置。

定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。

如果A[i] * A[i] < A[j] * A[j] 那么result[k–] = A[j] * A[j]; 。

如果A[i] * A[i] >= A[j] * A[j] 那么result[k–] = A[i] * A[i]; 。

如下所示:
在这里插入图片描述

class Solution {
    public int[] sortedSquares(int[] nums) {
        int len = nums.length; //数组长度
        int[] result = new int[len]; //新数组
        int left = 0, right = len-1;
        int k = len-1; //新数组下标
        while(left <= right){
            //左右指针的值进行比较,大的就赋值入新数组,从末尾开始赋值,再移动左右指针
            if(nums[left] * nums[left] < nums[right] * nums[right]){
                result[k--] = nums[right] * nums[right];
                --right;
            }else{
                result[k--] = nums[left] * nums[left];
                ++left;
            }
        }
        return result;
    }
}

在这里插入图片描述

209. 长度最小的子数组 - 中等 - 10/13

209. 长度最小的子数组 - 中等

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

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

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


解析: 滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。

在这里插入图片描述

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left =0; // 滑动窗口起始位置
        int sum = 0; // 滑动窗口数值之和
        int result = Integer.MAX_VALUE; // 滑动窗口的长度
        for(int right=0; right<nums.length; right++){
            sum += nums[right];
            // 注意这里使用while,每次更新 left(起始位置),并不断比较子序列是否符合条件
            while(sum >= target){
                result = Math.min(result, right-left+1);
                sum = sum - nums[left]; // 这里体现出滑动窗口的精髓之处,不断变更left(子序列的起始位置)
                ++left;
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

在这里插入图片描述

59. 螺旋矩阵 II - 中等 - 10/13

59. 螺旋矩阵 II - 中等

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

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


解析:模拟过程,从左到右、从上到下、从右到左、从下到上填充。
在这里插入图片描述

class Solution {
    public int[][] generateMatrix(int n) {
        int left = 0, right = n-1, top = 0, bottom = n-1; //定义边界
        int[][] ans = new int[n][n];  //存放新数组
        int num = 1, end = n * n; //开始和结尾
        while(num <= end){
            for(int i=left; i<=right; i++) ans[top][i] = num++; //从左到右
            ++top;
            for(int i=top; i<=bottom; i++) ans[i][right] = num++; //从上到下
            --right;
            for(int i=right; i>=left; i--) ans[bottom][i] = num++; //从右到左
            --bottom;
            for(int i=bottom; i>=top; i--) ans[i][left] = num++; //从下到上
            ++left;                                    
        }
        return ans;

    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值