【学习笔记&思考】代码随想录02|数组|4-6节

学习了代码随想录第4节、5节、6节内容。

第4节是977有序数组的平方,方法包括暴力解法(先平方后排序)、官方给出的双指针(找到负数和非负数的分界线,利用类似归并排序和双指针,将平方后已经有序的子数组排序)、卡哥给出的双指针(与官方给出的双指针不同,是从两边开始)

题目的几个条件和要求:

①非递减排序的整数组nums(也就是说有可能存在相等)
②返回每个元素平方后组成的新数组,且按照非递减顺序排序

代码实现:手撕不来,一边查资料理解,一边默写下来(后面我尽量能够不参考资料手写下来)

学习了代码随想录第4节、5节、6节内容。

第4节是977有序数组的平方sortedSquares,方法包括暴力解法(先平方后排序)、官方给出的双指针(找到负数和非负数的分界线,利用类似归并排序和双指针,将平方后已经有序的子数组排序)、卡哥给出的双指针(与官方给出的双指针不同,是从两边开始)

题目的几个条件和要求:

①非递减排序的整数组nums(也就是说有可能存在相等,至少含1个元素吧)
②返回每个元素平方后组成的新数组,且按照非递减顺序排序

代码实现:手撕不来,一边查资料理解,一边默写下来(后面我尽量能够不参考资料手写下来)

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for ( int i = 0; i < nums.size(); i++) {  
        // 实际上你可以完整地写下来,让思维流动,遇到阻塞耐心解决
            nums[i] *= nums[i];
        }
        sort(nums.begin(), nums.end());
        return nums;
    }
};

这里的sort应该属于快速排序!
官方给出的双指针解法,有点抽象,从头一点一点分析来看。
因为nums.size()后面多次使用到,所以令n=nums.size()。negativa是最大负数下标,肯定是非负的,先令成-1,方便后面判断是否找到&存在。
然后就是一层for循环,嵌入if语句找到negative值,使用了break跳出循环的技巧。
紧接着是双指针,先定义了数组ans,分界线设置两个指针i和j,使用while循环,嵌套if语句。具体来看,while的循环条件i>=0 || j<n,补集为i<0 && j>=n,要知道最大索引下标为n-1,但为什么要用或?如果举一个[2]和[-2]的单元素数组例子,就明白了,当[2]时,i=-1,j=0符合(所以终于明白为什么要赋值-1了,赋给其他值是不行的),同理当[-2]时,i=0,j=1符合。这种条件考虑到了单元素的特例。同时0和n都是作为i和j移动的边界条件。
再来看if条件,4个条件中,第一个if条件对应全为正数(此时i=-1),第二个if条件对应全为负数(此时i=n-1,j则等于n且不会大于n),后面两个if条件则对应i,j均在数组范围内(即有负数,也有非负数的情况),取比较后的最小值。理解每一次的while循环,实际都是在取一个最小值放入ans中,因为在取的过程中,i和j是动态变化的(数组可能取着取着就变成了全正数或全负数,这个时候应该按照什么规则取值呢?所以给出了第一个第二个条件)。


对代码的把控,应该来自于对其深入细致反复的理解。

根据以上剖析,可以很轻松地写出代码:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        int negative = -1;
        for (int i = 0; i < n; i++) {
            if (nums[i] < 0) {
                negative = i;
            } else {
                break;
            }
        }
        vector<int> ans;
        int i = negative;
        int j = negative + 1;
        while (i >= 0 || j < n) {
            if (i < 0) {
                ans.push_back(nums[j] * nums[j]);
                j++;
            } else if (j == n) {
                ans.push_back(nums[i] * nums[i]);
                i--;
            } else if (nums[i] * nums[i] < nums[j] * nums[j]) {
                ans.push_back(nums[i] * nums[i]);
                i--;
            } else {
                ans.push_back(nums[j] * nums[j]);
                j++;
            }
        }
        return ans;
    }
};

卡哥给出的双指针法,明显就简单很多,也可以分析一下。与官方的方法不同点在于是从两端从最大值找起,对result新数组是从末尾开始填充(因此要先定下尺寸-和nums一样大小)。首先定义result,再定义k=nums.size()-1,因为后面要用k来指向result,并不断递减填充。但是每次填充i或j要么加要么减,只存在一种情况,所以后面跟着for循环语句同时初始化i和j,并在嵌入的if语句中分别更新i和j而不在for条件内更新。要更新的是i和j(分别在两个条件中更新)以及k(每个条件都更新)。不难写出代码:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result(nums.size(), 0);
        int k = result.size() - 1;
        for (int i = 0, j = nums.size() - 1; i <= j;) {
            if (nums[i] * nums[i] < nums[j] * nums[j]) {
                result[k--] = nums[j] * nums[j];
                j--;
            } else {
                result[k--] = nums[i] * nums[i];
                i++;
            }
        }
        return result;
    }
};

第5节讲了209长度最小的子数组minSubArrayLen,给了含n个正整数的数组和正整数s,找出长度最小的连续子数组和大于等于s,返回其长度否则返回零。有暴力解法和滑动窗口两种解法,滑动窗口来源于对暴力解的简化。

class Solution209_0 {
public:
	int minSubArrayLen(vector<int>& nums, int s) {
		int result = INT32_MAX;
		int sum = 0;
		int subLength = 0;
		for (int i = 0; i < nums.size(); i++) {
			sum = 0;
			for (int j = i; j < nums.size(); j++) {
				if (sum >= s) {
					subLength = (j - i + 1);
					result = result < subLength ? result : subLength;
					break;
				}
			}
		}
		return result == INT32_MAX ? 0 : result;
	}
};

滑动窗口解法:不断调整子数组的起始和终止位置

class Solution209_1 {
public:
	int minSubArrayLen(int s, vector<int>& nums) {
		int result = INT32_MAX;
		int sum = 0;
		int i = 0;
		int subLength = 0;
		for (int j = 0; j < nums.size(); j++) {
			sum += nums[j];
			while (sum >= s) {
				subLength = (j - i + 1);
				result = result < subLength ? result : subLength;
				sum -= nums[i++];
			}
		}
		return result == INT32_MAX ? 0 : result;
	}
 };

第6节是59螺旋矩阵II,依据分析可以给出代码,因为关注点在代码技巧而不是算法的巧妙,所以不做详细解释了:

class Solution59_0 {
	vector<vector<int>> generateMatrix(int n) {
		vector<vector<int>> res(n, vector<int>(n, 0));
		int startx = 0, starty = 0;
		int loop = n / 2;
		int mid = n / 2;
		int count = 1;
		int offset = 1;
		int i, j;
		while (loop--) {
			i = startx;
			j = starty;
			for (j = starty; j < n - offset; j++) {
				res[startx][j] = count++;
			}
			for (i = startx; i < n - offset; i++) {
				res[i][j] = count++;
			}
			for (; j > starty; j--) {
				res[i][j] = count++;
			}
			for (; i > startx; i--) {
				res[i][j] = count++;
			}
			startx++;
			starty++;
			offset++;
		}
		if (n % 2) {
			res[mid][mid] = count; // n * n
		}
		return res;
	}
};

第5节和第6节还有对应的4道扩展题,其中水果成篮问题有一点头疼,后面再写专门文章解释分析。以上就是代码随想录Day2的内容了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值