一篇通关代码随想录 - 数组

二分查找

704. 二分查找

思路

题目要求

  • 数组为有序数组
  • 数组中无重复元素
  • 以上则可以考虑使用二分查找

思路一 左闭右闭[ ] 突破界限

  • target在区间[left, right]范围内,要考虑以下几点
  • left = 0, right = nums.length - 1
  • while (left <= right) 要使用 <= ,因为如果target出现在尾端,left == right时,mid才能取到尾端元素
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
  • 如果待查找的元素不存在,那么left最后的指向,是比target大的最近的元素位置,或者是超出数组长度的位置。
  • 如果待查找的元素不存在,right最后的指向,是比target小的最近的元素位置,或者是在数组首元素左边的位置。

思路二 左闭右开[ )

  • target在区间[left, right)范围内,要考虑以下几点
  • left = 0, right = nums.length
  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

代码

思路一

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

思路二

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

复杂度

时间复杂度:O(logn) 证明二分查找的时间复杂度
空间复杂度:O(1)

相似题目

35. 搜索插入位置
34. 在排序数组中查找元素的第一个和最后一个位置
69. x 的平方根
367. 有效的完全平方数


移除元素

27. 移除元素

思想

题目要求

  • 空间复杂度为O(1)
  • 移除目标元素后,其他的元素应该排在数组的前面
  • 最后返回这些其他元素的个数

思路 重构数组

  • 第一个指针指向待填充的数组位置,从0开始
  • 第二个指针遍历数组,寻找值 != val的元素
  • 直到后,将该值填充到第一个指针的位置处,第一个指针向后移动一位

代码

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

复杂度

时间复杂度:O(n)
空间复杂度:O(1)

相似题目

26. 删除有序数组中的重复项


有序数组的平方

977. 有序数组的平方

思路

  • 原数组nums以排好序,从小到大
  • 题目要求的是返回每个数字的平方,并按从小到大的顺序返回数组
  • 所以平方的最大值有可能是原数组的第一个元素或最后一个元素
  • 一个指针指向首元素,一个指向尾元素,进行比较,谁大谁填充到平方数组末尾

代码

class Solution {
    public int[] sortedSquares(int[] nums) {
        int l = 0;
        int r = nums.length - 1;
        int[] result = new int[nums.length];
        int index = result.length - 1;
        while(l < r){
            if(nums[r] * nums[r] >= nums[l] * nums[l]){
                result[index--] = nums[r] * nums[r];
                r--;
            }else{
                result[index--] = nums[l] * nums[l];
                l++;
            }
        }
        result[0] = nums[l] * nums[l];
        return result;
    }
}

复杂度

时间复杂度:O(n)
空间复杂度:O(n)

相似题目

88. 合并两个有序数组
283. 移动零
203. 移除链表元素
844. 比较含退格的字符串
977. 有序数组的平方


长度最小的子数组

209. 长度最小的子数组

思路

符合条件的子数组要满足以下的几个条件

  • 数组总和大于等于target
  • 要求子数组是连续的

然后从符合条件的子数组中找出长度最小的子数组,并返回其数组长度

思路一 暴力求解

  • 通过2个for循环确定子数组的左右边界
  • 第一个for用来确定子数组的左边界
  • 第二个for用来确定子数组的右边界
  • 只要满足条件的子数组,就记录他们的数组长度,统计出最小的长度。
  • 注意: 提示中 1 <= nums.length <= 10^5,暴力求解有可能超时。

思路二 滑动窗口

窗口,就是满足连续子数组和大于等于target的一个区间
滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果,即长度最小的子数组。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int i = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;
        for(int j = 0; j < nums.length; j++){
            sum += nums[j];
            while(sum >= target){
                int len = j - i + 1;
                result = len < result ? len : result;
                sum -= nums[i++];
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

复杂度

时间复杂度:O(n)
空间复杂度:O(1)

相似题目

904. 水果成篮
76. 最小覆盖子串
718. 最长重复子数组


螺旋矩阵II

59. 螺旋矩阵 II

思路

思路一 由外到内 逐圈填充

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

由外向内一圈一圈这么画下去。按照左闭右开的原则来填充。
在这里插入图片描述

那么要填充多少圈呢?

  • 观察可以发现,填充一圈之后,待填充的数组高度和宽度- 2
  • 直到待填充数组的高度和宽度都为0时,说明数组全部填充完毕
  • 例如:n = 2, 即高度、宽度都为2,那么只需1圈就可以全部填充完毕,即2 / 2 = 1
  • 当 n 为奇数时,在进行 n/2 (取整) 次填充后,最后只会剩下一个待填充的位置,进行额外的一次填充即可。

思路二 四个守卫,依次抢占领土

  • 一共有 四个守卫,他们依次循环占领数组的各行或各列, 占领各行, 占领各列
  • 守卫只能从左到右,依次占领这一行,遇到 则停止。停止之后,说明这一行已经被占领,之后从下一行开始。
  • 守卫只能从上到下,依次占领这一列,遇到 则停止。停止之后,说明这一列已经被占领,之后从当前占领的左边开始。
  • 守卫只能从右到左,依次占领这一行,遇到 则停止。停止之后,说明这一行已经被占领,之后从上一行开始。
  • 守卫只能从下到上,依次占领这一列,遇到 则停止。停止之后,说明这一列已经被占领,之后从当前占领的右边开始。

代码

思路一

class Solution {
    public int[][] generateMatrix(int n) {
        int x = 0;
        int y = 0;
        int i, j;
        int count = 1;
        int offset = 1;
        // 循环圈数 n 为 偶数  则循环圈数正好是整数
        int loop = n / 2;
        int[][] a = new int[n][n];
        while(loop-- > 0){
            //左到右
            for(j = y; j < n - offset; j++){
                a[x][j] = count++;
            }

            //上到下
            for(i = x; i < n - offset; i++){
                a[i][j] = count++;
            }

            //右到左
            for(; j > y; j--){
                a[i][j] = count++;
            }

            //下到上
            for(; i > x; i--){
                a[i][j] = count++;
            }

            x++;
            y++;
            offset++;
        }
        if(n % 2 != 0){
            a[x][y] = count;
        }
        return a;
    }
}

思路二

class Solution {
    public int[][] generateMatrix(int a) {
        int m = a;//m行
        int n = a;//n列
        int l = 0, r = a - 1;
        int low = 0, high = m - 1;
        int[][] ans = new int[m][n];
        int count = 1;
        while(true){
            //从左到右
            for(int i = l; i <= r; i++) ans[low][i] = count++;
            if(++low > high) break;

            //从上到下
            for(int i = low; i <= high; i++) ans[i][r] = count++;
            if(--r < l) break;

            //从右到左
            for(int i = r; i >= l; i--) ans[high][i] = count++;
            if(--high < low) break;

            //从下到上
            for(int i = high; i >= low; i--) ans[i][l] = count++;
            if(++l > r) break;
        }
        return ans;
    }
}

复杂度

时间复杂度 O(n^2) 模拟遍历二维矩阵的时间
空间复杂度 O(1)

相似题目

54.螺旋矩阵

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值