数组系列专题

数组类型题目常用解决方法

待记录

例题

leetcode59. 螺旋矩阵 II

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

思路分析

生成一个 n×n 空矩阵 mat,随后模拟整个向内环绕的填入过程:

  • 定义当前左右上下边界 l,r,t,b,初始值 num = 1,迭代终止值 tar = n * n;
  • 当 num <= tar 时,始终按照 从左到右 从上到下 从右到左 从下到上 填入顺序循环,每次填入后:执行 num += 1:得到下一个需要填入的数字;
  • 更新边界:例如从左到右填完后,上边界 t += 1,相当于上边界向内缩 1。 使用num <= tar而不是l < r || t < b作为迭代条件,是为了解决当n为奇数时,矩阵中心数字无法在迭代过程中被填充的问题。 最终返回 mat 即可。
class Solution {
public:
    //同54题,只是该题规定是正方形,里面不必判断元素数量
    vector<vector<int>> generateMatrix(int n) {
        int l = 0, r = n - 1, t = 0, d = n - 1;//上下左右
        vector<vector<int>> ans(n, vector<int>(n));
        int total = n * n, cnt = 1;//计数:遍历多少元素了

        while(cnt <= total){
            //左至右(勿忘在里面判断已遍历的数量,否则长方形的矩阵会多打印)
            for(int i = l; i <= r; i++) ans[t][i] = cnt++;
            t++;
            //上至下
            for(int i = t; i <= d; i++) ans[i][r] = cnt++;
            r--;
            //右至左
            for(int i = r; i >= l; i--) ans[d][i] = cnt++;
            d--;
            //下至上
            for(int i = d; i >= t; i--) ans[i][l] = cnt++;
            l++;
        }
        return ans;
    }
};

其他按圈缩的解法,详见解答

leetcode54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

思路分析

同上题,唯一要注意的是此处的矩阵可能是长方形,此时里面的遍历要添加元素个数的判断,否则会出现重复遍历。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();//m行n列
        int l = 0, r = n - 1, t = 0, d = m - 1;//上下左右
        vector<int> ans;
        int total = m * n, cnt = 1;//计数:遍历多少元素了

        while(cnt <= total){
            //左至右(勿忘在里面判断已遍历的数量,否则长方形的矩阵会多打印)
            for(int i = l; i <= r && cnt<= total; i++){
                ans.push_back(matrix[t][i]);
                cnt++;
            }
            t++;
            //上至下
            for(int i = t; i <= d && cnt<= total; i++){
                ans.push_back(matrix[i][r]);
                cnt++;
            }
            r--;
            //右至左
            for(int i = r; i >= l && cnt<= total; i--){
                ans.push_back(matrix[d][i]);
                cnt++;
            }
            d--;
            //下至上
            for(int i = d; i >= t && cnt<= total; i--){
                ans.push_back(matrix[i][l]);
                cnt++;
            }
            l++;
        }
        return ans;
    }
};

其他解法详见解答

leetcode665. 非递减数列

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

思路

拿到今天这个题,看到是个Easy,就没有想太多了。直接判断是不是只出现了一次下降!迅速写出下面的代码,题目给的两个测试用例都通过了,那么就提交!
没想到,啪!直接来了个解答错误,很快啊! 题目说该代码没有通过测试用例 [3,4,2,3]。仔细一想还真是,虽然该数组中只出现了一次下降,但是无论调整其中的一个数字都不能得到一个单调上升的数组。
那么,这题就有讲究了。下面我举了例子,相信我,下面的分析不难,你看完一定能懂。

  举例分析

首先,看下面的几个测试用例,它们都因为数字 2 的出现,导致数组是非单调递增的。

  例①: 4, 2, 5 
  例②: 1, 4, 2, 5 
  例③: 3, 4, 2, 5 

当数组中出现 2时,破坏了数组的单调递增。为了让数组有序,我们需要对 2 或者 4 进行调整:

  第①个用例,我们可以 把 4 调小到 <= 2  或者 把 2 调大到 4、5 ,使数组有序。 
  第②个用例,我们可以 把 4 调小到 1、2或者 把 2 调大到 4、5 ,使数组有序。 
  第③个用例,我们必须 把 2 调大到 4、5,才能使数组有序:我们不能把 4 调整为一个 <=2 的数字,因为 4 前面的元素是 3.

归纳总结
当 nums[i] 破坏了数组的单调递增时,即 nums[i] < nums[i - 1] 时,为了让数组有序,我们发现一个规律(在上面三个例子中, nums[i] 都为 2, nums[i -1] 都为 4):

  如例①的情况,当 i = 1 ,那么修改 num[i- 1] ,不要动 nums[i],因为nums[i]后面的元素是啥我们还不知道呢,少动它为妙。 
  如例②的情况,当 i > 1 时,我们应该优先考虑把 nums[i - 1]调小到 >= nums[i - 2] 并且 <= nums[i]。同样尽量不去修改 nums[i] ,理由同上。 
  如例③的情况,当 i >1 且 nums[i] < nums[i - 2] 时,我们无法调整 nums[i - 1] ,我们只能调整 nums[i] 到nums[i - 1] 。

找到nums[i-1]>当前nums[i]时,也要判断前第二个数nums[i-2]与当前数nums[i]的大小关系

class Solution {
public:
    bool checkPossibility(vector<int>& nums) {
        int n = nums.size(), count = 0;
        for(int i = 1; i < n; i++){
            if(nums[i - 1] > nums[i]){//出现前一个>后一个数时
                if(i == 1 || nums[i - 2] <= nums[i]){//勿忘判断当前数与往前两个数的大小关系
                    nums[i - 1] = nums[i];
                }else{
                    nums[i] = nums[i - 1];
                }
                count++;//找到一个降序对就+1
            }
        }
        return count <= 1;
    }
};

另一个思路

或者 不赋值直接 依然统计降序对个数,只是把特殊情况单独写出来判断

  • 遍历数组,初始count = 0,如果当前元素值比它下一个元素值大,则count += 1,当count > 1时,直接返回false。
  • 另外,在遍历数组的过程中,如果遇到 “特殊情况”,可以直接返回false;当循环正常结束则返回true。
    特殊情况解释:
    首先看一个简单的例子:[2, 4, 0, 1]

当下标 i == 2 时,nums[i] < nums[i-1],也就是 0 < 4,这时我们要保证数组非递减有两种选择:

选择 1:把 0 放大,并且保证 0 后面的数不能比 4 小,0 前面的数均是非递减的;

选择 2:把 4 缩小,并且保证 4 前面的数不能比 0 大,4 后面的数均是非递减的。

换言之,当 0 < 4时,如果以上两个选择中的保证同时不满足,说明该数组肯定无法通过只修改一个元素而变为非递减数组,该情况即为上述提到的 “特殊情况”。对于本例,有:当 0 < 4时,0 后面的数为 1,1 小于 4,不满足选择 1 的保证;且 4 前面的数为 2,4 后面的数为 0,0 小于 2,不满足选择 2 的保证。

综上,我们可以把 “特殊情况” 归纳为:

if nums[i] < nums[i-1]:          # 当 0 < 4 时
    if nums[i+1] < nums[i-1] and nums[i-2] > nums[i]:         # 1 < 4 and 2 > 0
        return False             # 不用继续遍历,可直接返回false

另外需要注意数组下标不要越界。

完整代码

class Solution:
    def checkPossibility(self, nums: List[int]) -> bool:
        count = 0
        for i in range(1,len(nums)):
            if nums[i] < nums[i-1]:
                count += 1
                if i+1 < len(nums) and i-2 >= 0:
                    if nums[i+1] < nums[i-1] and nums[i-2] > nums[i]:
                        return False
            if count > 1:
                return False
        return True
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值