IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II -> 正文阅读

[数据结构与算法]977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

977. 有序数组的平方

给你一个按?非递减顺序?排序的整数数组?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)?的算法解决本问题

?看到题目第一个想法,直接每个元素求平方,然后用无敌的sort()解决问题。元素遍历时间复杂度是O(n),sort()排序是O(nlogn),搞定。

但是不知道是LeetCode没有import Array包还是怎么的,不能识别sort函数,那行吧,就用双指针吧。

其实看到题目有“顺序”这个关键词,不管是顺序还是逆序,只要是递增或者递减(可有重复元素),就该想到双指针了。我的想法是在负数区设置一个向左移动的负指针,正数区设置一个向右移动的正指针,每次移动指针就比较指针指向的数的平方的大小,将小的数放到按顺序新数组里,不就解决了吗?

接下来考虑指针“碰壁”的情况。当负指针指向第一个元素,或者正指针指向最后一个元素后,对应指针就不能移动了,否则会出现索引超出数组范围的情况。“碰壁”后,只需要继续移动另一个指针,把指针指向元素求平方后依次放入新数组即可。

不过还需要考虑几种特殊情况。首先题目里nums数组的长度是能为1的,而正负指针要能指向两个数。另外数组完全可以完全没有负数或者完全没有正数,这种情况下负指针或正指针就不存在了,也要单独考虑。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n = nums.length;
        if(n == 1){
            nums[0] *= nums[0];
            return nums;
        }
        if (nums[0] >= 0){
            for(int i = 0; i < n; i++){
                nums[i] *= nums[i];
            }
            return nums;
        }
        
        int[] newnums = new int[n];
        if(nums[n - 1] < 0){
            for(int i = 0; i < n; i++){
                newnums[i] = nums[n - 1 - i] * nums[n - 1 - i];
            }
            return newnums;
        }

        int pos = -1;
        for(int i = 0; i < n; i++){
            if(nums[i] >= 0){
                pos = i;
                break;
            }
        }

        int neg = pos - 1;
        int i = 0;
        while (neg >= 0 && pos <= n - 1){
            if (nums[pos] > -nums[neg]){
                newnums[i++] = nums[neg] * nums[neg];
                neg--;
            } else {
                newnums[i++] = nums[pos] * nums[pos];
                pos++;
            }
        }
        if (neg == -1){
            while(pos < n){
                newnums[i++] = nums[pos] * nums[pos];
                pos++;
            }
        }
        if (pos == n){
            while(neg >= 0){
                newnums[i++] = nums[neg] * nums[neg];
                neg--;
            }
        }
        return newnums;
    }
}

看起来比较复杂!后面看了其他人的解答,才发现双指针完全可以不从中间移动到两边,而是从两边移动到中间,这样边界问题就可以不用考虑得那么复杂。

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

(参考:代码随想录)?

双指针的题,从中间到两边,从两边到中间的都有,以后还是要更灵活地考虑问题。

另外学到个小技巧,

newnums[i++] = nums[pos] * nums[pos];
pos++;

这种写法略繁琐,但两个pos++的话指针就会移动两次了。但其实完全可以

newnums[i++] = nums[pos] * nums[pos++];

哈哈!确实比较刁钻,只写一个++,一开始没想到确实脑筋还不够灵活。

209. 长度最小的子数组

给定一个含有?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

进阶:

  • 如果你已经实现?O(n)?时间复杂度的解法, 请尝试设计一个?O(n log(n))?时间复杂度的解法。

?看到连续子数组,基本就可以想到是要用滑动窗口了。简单来说就是设置左右两个指针,分别作为窗口的左右边界。先设置初始窗口,左边界为第一个元素,右边界为第一个满足条件的右边界元素。然后向右移动左指针,直到不满足条件,再向右移动右指针,直到满足条件。不断更新满足条件的lenmin,直到结束。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left = -1, right = -1;
        int n = nums.length;
        
        if (n == 1){
            if (nums[0] >= target)
                return 1;
            else
                return 0;
        }
        
        int sum = 0;
        while (sum < target && right < n - 1){
            sum += nums[++right];
        } 

        if (sum < target)
            return 0;
        
        int lentmp = right - left;
        int lenmin = lentmp;

        while (left < right){
            while(sum >= target){
                sum -= nums[++left];
                lentmp--;
            }
            lenmin = lenmin <= (lentmp + 1) ? lenmin : (lentmp + 1);

            while (sum < target && right < n - 1){
                sum += nums[++right];
                lentmp++;
            }

            if (right == n - 1){
                while(sum >= target){
                    sum -= nums[++left];
                    lentmp--;
                }
                lenmin = lenmin <= (lentmp + 1) ? lenmin : (lentmp + 1);
                break;
            }
        }
        return lenmin;
    }
}

一开始没想好结束循环的条件,一直无限循环,直到debug才发现。后面实在想不出怎么结束,只好暴力解除循环,代码看起来就非常inelegant。如果一开始就想到会出现难以结束循环的情况,应该直接用for循环:

class Solution {

    public int minSubArrayLen(int s, 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 (sum >= s) {
                result = Math.min(result, right - left + 1);
                sum -= nums[left++];
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

(参考:代码随想录)?

至于进阶中说尝试设计?O(n log(n))?时间复杂度的解法,想不出来。问了大佬,说是先得到前缀和数组构成非递减数组,然后采用二分查找。暂时想不出来,先放放,哈哈。

59. 螺旋矩阵 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

?

这道题就有点略折磨了,边界问题搞得欲仙欲死,需要勤debug才能发现问题在哪:

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] res = new int[n][n];
        int left = 0; 
        int right = n - 1;
        int up = 0;
        int down = n - 1;
        int digit = 1;
        int i, j;
        while (digit <= n * n){
            for (i = left; i <= right; i++){
                res[up][i] = digit++;
            }
            
            for (j = up + 1; j <= down; j++){
                res[j][right] = digit++;
            }
            for (i = right - 1; i >= left; i--){
                res[down][i] = digit++;
            }
            for (j = down - 1; j > up; j--){
                res[j][left] = digit++;
            }
            left++;
            right--;
            up++;
            down--;
        }
        return res;
    }
}

?思路就是设置四条边的边界。把数字顺序看成蚂蚁在矩阵格子上爬行的路径,那么把蚂蚁正在爬行的四条边看成left,right,up,down。当蚂蚁在up、down边上爬行的时候,索引i会变化。当蚂蚁在left、right边上爬行时,索引j变化。再具体考虑每条边的边界,避免撞格子和漏格子。首先在up边爬行时,边界是left和right。当蚂蚁爬完up边,改在right边爬行时,由于 (up,right) 这个格子已经被爬过了,起点应该从up+1开始,到down结束。在down边爬行时, (down,right) 这个格子被爬过了,从right-1开始,到left结束。最后爬行的是left边,注意,不仅(down,left)格子被爬过,(up,left)这个格子也在最开始被蚂蚁爬过,所以应从down - 1开始,到up+1结束。这样一来,蚂蚁就爬完了最外层一圈,开始爬次外层。次外层边界是left+1,right-1,up+1,down-1,除了边界发生了变化,和在最外层爬行没什么不同。循环!

循环终止条件本来想写left和right重合或相贴,up和down重合或相贴时结束,分成n为奇数和n为偶数两种情况(后面想了想倒也不必,循环条件设置成left<=right&&up<=down即可,n为奇数时会空跑一次循环,结果也没问题)。后面一想,数字变成n_{}^{2}时结束循环不就完事了?模拟了一下,果然不管n是奇数还是偶数还是1,都能成功结束循环,搞定!

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 12:25:10  更:2022-10-31 12:28:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:43:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码