【算法训练 day2 有序数组的平方、长度最小的子数组、螺旋矩阵】


一、有序数组的平方-LeetCode 977

Leecode链接: leetcode 977
文章链接: 代码随想录
视频链接: B站

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

示例:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]

思路

初始数组是一个有序数组,那么最大的数可能在数组的两端,因为最小的数平方后可能更大,例如:[-10,1,4,9],-10<9,但100>81。那么考虑使用双指针,一个最左一个最右,依次对比平方后的结果,将大的数插入新的数组中。个人在这里直接尾插,然后使用reverse翻转数组,结果一样的,也可以使用数组下标赋值,但那要用到三个指针并且新的数组所有数据需要初始化为0。代码如下:

实现代码

1.双指针

//cpp
vector<int> sortedSquares(vector<int>& nums) {
	int left, right;
	vector<int> res;
	left = 0;
	right = nums.size() - 1;

	while (left <= right) {
		if (nums[left] * nums[left] <= nums[right] * nums[right]) {
			res.push_back(nums[right] * nums[right]);
			right--;
		}
		else {
			res.push_back(nums[left] * nums[left]);
			left++;
		}
	}
	reverse(res.begin(),res.end());
	return res;
}

2.三指针

//cpp
int search(vector<int>& nums) {
	int k = nums.size() - 1;
    vector<int> res(nums.size(), 0);
    for (int i = 0, j = nums.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
        if (nums[i] * nums[i] < nums[j] * nums[j])  {
            res[k--] = nums[j] * nums[j];
            j--;
        }
        else {
            res[k--] = nums[i] * nums[i];
            i++;
        }
    }
    return res;
}

个人问题

1.个人在while条件判断这里一开始没有添加=,代码不够精简,实际添加=是可行的,因为nums[left] * nums[left] <= nums[right] * nums[right]保包含了数组存在重复数据可能。

总结

整体比较简单,需要注意数组的边界判断。算法时间复杂度为O(n),空间复杂度为O(1)。


二、长度最小的子数组-LeetCode 209

Leecode链接: LeetCode 209
文章链接: 代码随想录
视频链接: B站

给定一个含有 n 个正整数的数组和一个正整数 target。
找出该数组中满足其总和大于等于 target 的长度最小的连续子数组
并返回其长度。如果不存在符合条件的子数组,返回0。

示例:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

思路

题目要求子数组和大于等于target且长度尽量短,考虑到子数组必定是连续的,那么使用两个指针分别指向窗口两端,这样就表示这个子数组的长度;两个指针移动时根据情况进行加或减操作,保证子数组的和能够增加减少。同时,取一个中间量记录子数组最小的长度,直到遍历到数组结尾退出并返回这个最小长度。

实现代码

快慢指针(滑动窗口)

//cpp
int minSubArrayLen(int target, vector<int>& nums) {
	int sum = 0;//记录数据总和

	int res = INT32_MAX;//初始化一个最大值,保证第一个长度就是最小值

	int len = 0;//窗口的长度

	int size = nums.size();

	int j = 0;//j为慢指针

	for (int i = 0; i < size; i++) {//i为快指针
		sum += nums[i];
		while (sum >= target) {
			len = (i - j + 1);
			res = len < res ? len : res;
			sum -= nums[j++];//这里含义是将慢指针右移
		}
	}
	
	if (res == INT32_MAX) {//如果成立,表示没有满足的子数组
		return 0;
	}
	return res;
}

个人问题

在实现代码时,判断子数组的和大于target时进行长度计算以及删除慢指针的数据这段代码没有实现出来,只想到先使用if条件判断,然后使用循环。应该直接使用while就能实现,对代码熟练度还不够。

while循环体内可以不用考虑慢指针是否大于快指针,只需要在值是否大于等于target时记录最小值即可,因为当慢指针大于快指针表示子数组的和已经被置为0了。

总结

题目本质还是双指针的应用,外层for循环用来移动快指针,内层while循环用来移动慢指针,从而达到滑动窗口的效果。这里对元素只是加了一次与减一次的操作,并没有在while里对整个数组进行操作,所以复杂度为O(2n)也就是O(n),空间复杂度为O(1)。


三.螺旋矩阵-LeeCode 59

Leecode链接: LeetCode 59
文章链接: 代码随想录
视频链接: B站

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

示例:

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

思路

题目需要输出一个二维数组且按顺时针排列,一个一个数组赋值不太现实,需要自己画一个图,总结图形规律来进行赋值。首先考虑特殊形式赋值的一个过程,即单独给最外层赋值时,横坐标增加的赋值区间为i<n-1,纵坐标增加的赋值区间也为j<n-1;横坐标递减的赋值区间为i>0,纵坐标递减的赋值区间为j>0。但这只是最外一层,当n>2时,至少两层,所以需要嵌套循环,因为内层与外层起始坐标不一样,每所以经过一个循环需要将起始坐标自加1。同样单个边赋值的区间也需要依次递减

实现代码

//cpp
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
        int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
        int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
        int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
        int count = 1; // 用来给矩阵中每一个空格赋值
        int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
        int i,j;
        while (loop --) {
            i = startx;
            j = starty;

            // 下面开始的四个for就是模拟转了一圈
            // 模拟填充上行从左到右(左闭右开)
            for (j; j < n - offset; j++) {
                res[i][j] = count++;
            }
            // 模拟填充右列从上到下(左闭右开)
            for (i; i < n - offset; i++) {
                res[i][j] = count++;
            }
            // 模拟填充下行从右到左(左闭右开)
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            // 模拟填充左列从下到上(左闭右开)
            for (; i > startx; i--) {
                res[i][j] = count++;
            }

            // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
            startx++;
            starty++;

            // offset 控制每一圈里每一条边遍历的长度
            offset += 1;
        }

        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }

个人问题

编写代码时陷入庞杂的条件判断中,没有理清楚各个步骤应该做的事,代码没有自己写出来。

总结

非算法题,考验对数组边界的判断以及对代码的掌控能力。时间复杂度O(n^2),空间复杂度O(1)。

  • 24
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值