leetcode简单数组题【二】(977, 209, 59)

本文介绍了如何使用双指针法解决编程题目,包括有序数组的平方问题,通过快速排序和双指针比较元素绝对值,以及长度最小的子数组问题,强调了逻辑处理在算法中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次的题目977和209主要考察双指针的应用、


首先是第997题:有序数组的平方

题目:

给你一个按 非递减顺序 排序的整数数组 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 已按 非递减顺序 排序

这里有一点就是这个非递减顺序,就是递增,刚开始我还懵了一下

最简单的方法就是把每个元素全部平方然后使用快速排序,详见下面代码,这里就不细讲,看不懂的话可以复习一下快速排序(我这里的分区法是挖空法,还有另外两种方法可以使用)

class Solution {
public:
    int partition(vector<int>& nums,int start, int end){
	int pivot = nums[start];
	while (start < end){
		while (nums[end] > pivot && start < end){
			end--;
		}
		nums[start] = nums[end];
		while (nums[start] <= pivot && start < end){
			start++;
		}
		nums[end] = nums[start];
	}
	nums[start] = pivot;
	return start;
}

void quick_sort(vector<int>& nums, int start,int end){
	if (start >= end){
		return;
	}
	int mid = partition(nums, start, end);
	quick_sort(nums, start, mid-1);
	quick_sort(nums, mid+1, end);
}

vector<int> sortedSquares(vector<int>& nums){
	int pivot = nums[0];
	
	for (int i = 0; i < nums.size(); i++){
		nums[i] = nums[i]*nums[i];
	}
	quick_sort(nums, 0, nums.size()-1);
	return nums;
}
};

第二种方法是比较简单的解法:双指针法

因为输入的数组就是从小到大的,所以我们需要顾忌的排序其实主要是整数和负数绝对值之间的比较,绝对值更大的数平方肯定就会更大,要是有负数存在的话,只可能在两边然后中间有0。

设置一个left指针从数组头开始,right指针从数组尾部开始,每次比较left和right位置元素的绝对值大小,每次选择最大的放在最右边(因为最大的只有可能在两边。当没有负数的时候,最小值在左边,有负数的时候不知道在中间什么地方,所以我们每次把最大的元素找到放在结果数组的最右边)

新开一个result数组(初始化大小和nums大小一样)用来存放结果,维护一个result的索引k用来放入数据,初始为最右边,当left指针指的元素大于right指针指向的元素,就把left指针指向的元素(最大)放到result数组k处,将left指针向右移动;反之,如果right指针指向的数字更大就把这个更大的元素放在k索引处,right向左移动一次。每次成功放入result数组后,索引向前一个用来放下一个元素。当left>right的时候说明所有元素都比较完了,结束循环返回result。这里left=right仍然循环是因为方便放元素到result里,不然有一个元素漏掉了没放,循环外面处理就更麻烦。

下面是c++代码

class Solution {
public:
vector<int> sortedSquares(vector<int>& nums){
	int left = 0;
	int right = nums.size()-1;
	vector<int> result(nums.size(),0);
	int k = nums.size()-1;
	while (left <= right){
		if (abs(nums[left]) > abs(nums[right]) ){
			result[k--] = pow(nums[left], 2);
			left++;
		}else{
			result[k--] = pow(nums[right], 2);
			right--;
		}
 }
	return result;
}
};

 接下来是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

 这道题也是使用双指针的思想

 因为要寻找长度最小的子数组,且元素之和一定要大于等于target,这里提醒大家一定要认真审题,我刚开始以为是等于target,哐哐哐写完以后发现错了还反应不过来。

同样的,设置一个左指针i一个右指针j,维护一个变量sum初始化为0,用来计算和,接着维护一个变量length用来记录最小长度,初始化为数组的大小+1,因为就算全部数组元素加起来才刚好等于target也应该是数组大小,不可能大过数组大小还+1。

接下来移动指针j,每次移动的时候sum都要加上j值,当sum刚刚好大于或等于target的时候开始执行操作,设置一个while循环(因为要是这时候j指针再往后移动就是没必要的增加数组长度,后面再加肯定更比target大了)如果当前子数组长度小于length,就把length设置为子数组长度(注意子数组长度是j-i+1)然后将i往右边移动一个单位,减去前一个值以后,继续看target和数组和之间大小关系(这里可能没想到,给个提示,比如数组[1,2,3,1],target = 4,最先出来的肯定是[1,2,3], 但是实际上仅仅i往后面移动一个单位的话是数组[2,3]满足比target大且比之前那个数组长度小)最后i和j从头遍历到尾后跳出循环,准备返回长度,如果length还是之前的数组大小+1的话,必定是没有找到子数组,设置length = 0,然后最后统一返回length。

下面是c++代码:
 

class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums){
	int i = 0;
	int length = nums.size()+1;
	int sum = 0;
	for (int j = 0; j<nums.size(); j++){
		sum += nums[j];
		while (sum>=target){
			if (j-i+1 < length){
				length = j-i+1;
			}
			sum -= nums[i++];
		}
	}
	if (length == nums.size()+1){
		length = 0;
	}
	return length;
} 
};

59题主要考察对逻辑的处理

题目:

给你一个正整数 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

其实这道题本质上最难的是处理边界条件,要统一处理每一条边的边界条件,这道题我选择对于每一行,不处理最后一个值,下一行从之前的最后一个值(也就是下一行开头)开始。以示例为例,比如第一行我就把1 2放入数组,然后下一行就是3 4,以此类推 。

在处理之前,因为每一圈起点和终点都不一样,所以我们首先要维护两个变量:startx和starty作为每次出发的起点,初始化为0,接着是count作为插入结果的值,每次加1,初始化为1就好。然后就是插入的位置坐标,这里我设置为i和j,分别初始化为0。设置result数字,初始化大小填充0进去,最后要设置的就是偏移量offset,初始化为0,作用后面会讲。

不难发现,转圈的次数和n紧密相关,就是n/2次,但是这里有个问题,当n是奇数的时候,正方形容器(这里可以看那个示例)最最中间的值插入会被忽略,所以后面会额外处理这个。

最外层for循环是转圈次数

接下来开始遍历从左到右的行,我们习惯写[i][j],所以刚开始是写j的for循环。因为每一行最后一个元素不管,对于第一行j<n-1,第二行(第二圈循环)j<n-2,以此类推,所以减掉的数字就是偏移量offset,每次最外层循环结束后会累加。把count累加放入result数组对应位置中,因为是从左向右所以纵向位置还是starty不变,横向位置就是j的位置。

现在遍历右边从上到下的行,与前面结束条件一样,唯一不一样的是result索引为[i][j]因为前面j已经固定到横向终点的地方了,纵向移动的时候横坐标是不会变的。

然后遍历下面从右到左的行,这里与前面基本一样,唯一不同的点是结束条件是j要大于起点

最后以与上面相同的形式从下到上遍历左边

最后的最后,跳出外层for循环后,我们已经完成转圈插入的过程了,需要处理的是当n是奇数的时候插入中间的值,在本例中就是那个9,因为我们一直在增加startx和starty,虽然for循环结束了,startx和starty已经累加到第三圈起始点(这一“圈”只有一个值),之前最后一次插入值的时候count也已经累加1了,就直接作等result[starty][startx] = count即可,返回result数组,题目结束

下面是c++代码

class Solution {
public:
 vector<vector<int>> generateMatrix(int n) {
 	int startx = 0;
 	int starty = 0;
 	int offset = 1;
 	int count = 1;
 	int j = 0;
 	int i = 0;
 	vector <vector<int>> result(n, vector<int>(n,0));
 	for (int time = 0; time < n/2; time++){
 		for (j = startx;j < n-offset; j++){
 			result[starty][j] = count++;
		 }
		 for (i = starty;i < n-offset; i++){
		 	result[i][j] = count++;
		 }
		 for(;j > startx; j--){
		 	result[i][j] = count++;
		 }
		 for(;i > starty; i--){
		 	result[i][j] = count++;
		 }
		 offset++;
		 startx++;
		 starty++;
	 }
	 if (n%2){
	 	result[starty][startx] = count;
	 }
	 return result;
 }
 
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值