977.有序数组的平方
题目描述:给你一个按 非递减顺序 排序的整数数组
nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
对数组进行排序的方法:
方法一:冒泡排序
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
-
对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
-
重复以上的步骤,每次比较次数-1,直到不需要比较
-
外层循环i<nums.size()-1;内层循环j<nums.size()-1-i
#include<iostream> using namespace std; int main() { int arr[9] = { 4,2,8,0,5,7,1,3,9 }; for (int i = 0; i < 9 - 1; i++) { for (int j = 0; j < 9 - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } for (int i = 0; i < 9; i++) { cout << arr[i] << endl; } system("pause"); return 0; }
方法二:sort函数
-
但是sort函数默认升序排列, 可以使用greater<int>()降序排列:sort(arr,arr+5,greater<int>());
-
#include<iostream> #include<algorithm> using namespace std; int main() { int arr[]={2,4,5,3,1}; sort(arr,arr+5); for(int i=0;i<5;i++) { cout<<arr[i]; return 0; }
这道题如果这么解:结果会超出时间限制!为什么?
class Solution { public: vector<int> sortedSquares(vector<int>& nums) { for(int i=0;i<nums.size();i++) { nums[i]=nums[i]*nums[i]; } for(int i=0;i<nums.size()-1;i++) { for(int j=0;j<nums.size()-1-i;j++) { if(nums[j]>nums[j+1]) { int temp=nums[j]; nums[j]=nums[j+1]; nums[j+1]=temp; } } } return nums; } };
采用sort函数解:
class Solution { public: vector<int> sortedSquares(vector<int>& nums) { for(int i=0;i<nums.size();i++) { nums[i]=nums[i]*nums[i]; } sort(nums.begin(),nums.end()); return nums; } };
双指针法:
数组其实是有序的, 只不过负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。(因为首先判断出来的是数组的最大值,所以就倒序输入)
如果
A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。如果
A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int>result(nums.size(),0);//定义一个result数组,大小等于nums数组的大小,初始化值都是0
int k=result.size()-1;//k为result数组的下标,因为值是从大到小判断出来的,所以值倒序输入到result数组中,最大值存到最后一位
for(int i=0,j=nums.size()-1;i<=j;)
{
if(nums[i]*nums[i]>nums[j]*nums[j])
{
result[k]=nums[i]*nums[i];
i++;
k--;
}
else
{
result[k]=nums[j]*nums[j];
k--;
j--;
}
}
return result;
}
};
python中创建制定长度和初值的列表:
#一维列表的创建:
a = [None] * n # 创建一个初值为None的长度为n的列表a,None只是一种初值也可换成其他的初值。
# 二维列表的创建:
a = [[0 for col in range(m)] for row in range(n)] # 创建一个n*m的二维矩阵a,每个初值都是0
class Solution(object):
def sortedSquares(self, nums):
left=0
right=len(nums)-1
i=len(nums)-1
result=[None]*len(nums)#创建一个和原数组长度相等且初始值为None的数组接收最终结果
while left<=right:
if nums[left]**2>nums[right]**2:
result[i]=nums[left]**2
left +=1
else:
result[i]=nums[right]**2
right -=1
i -=1
return result
209.长度最小的子数组
题目描述:
给定一个含有
n
个正整数的数组和一个正整数target
。找出该数组中满足其总和大于等于
target
的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0
。
暴力解法:会超时
两层for循环:外循环i从0开始,内循环从i开始逐渐往后移动,并判断nums[i]+nums[j]是否达到目标值,如果大于等于target,记录下此时的数组长度,跳出此次循环;然后移动i,移动j,如果又找到了满足条件的数组,和之前的数组长度进行比较,取最小的。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int len=0;
int result=INT32_MAX;//INT32_MAX是C/C++语言中预定义的一个常量,它代表了32位有符号整数类型(int)所能表示的最大值,即2147483647
for(int i=0;i<nums.size();i++)
{
int sum=0;
for(int j=i;j<nums.size();j++)
{
sum+=nums[j];
if(sum>=target)
{
len=j-i+1;
if(len<result)result=len;
break;
}
}
}
if(result==INT32_MAX)return 0;
else{return result;}
}
};
注:
INT32_MAX是C/C++语言中预定义的一个常量,它代表了32位有符号整数类型(int)所能表示的最大值,即2147483647
暴力解法二:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX; // 最终的结果
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= s) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
注:
result = result < subLength ? result : subLength;
如果result<subLength,result更新为 subLength,否则result=result;
return result == INT32_MAX ? 0 : result;
判断result是否还是最大值,如果是return 0,否则return result.
也就是说这个语法的意思是,如果满足条件就返回:左边的值,不满足条件就返回:右边的值
滑动窗口法:
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢。
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
在本题中实现滑动窗口,主要确定如下三点:
- 窗口内是什么?
- 如何移动窗口的起始位置?
- 如何移动窗口的结束位置?
- 窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
- 窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
- 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
解题的关键在于 窗口的起始位置如何移动,如图所示:
class Solution {
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,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= s) {
subLength = (j - i + 1); // 取子序列的长度
result = result < subLength ? result : subLength;
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
- 使用while是因为:移动完i之后 ,仍然要继续判断缩小后的窗口里的值之和是否大于等于s,如果还大于等于s,则继续移动窗口的起始位置,即继续缩小窗口,即持续进行这个过程;如果使用if的话,如果sum>=s,更新完i之后就不再继续判断缩小后的窗口里的值之和是否大于等于s,而是去移动终止位置了。
- sum-=nums[i++];//等同于sum-=nums[i];i++;
Python解法(自己的解法):
class Solution(object):
def minSubArrayLen(self, target, nums):
i=0
k=0
sum=nums[0]
size=len(nums)
count=1
result=[]
while i<=k and k<size:
if sum<target:
k+=1
if k<size:
count+=1
sum+=nums[k]
else:
result.append(count)
sum-=nums[i]
i+=1
count-=1
if result:
result=sorted(result)
return result[0]
else:
return 0
59.螺旋矩阵
题目描述:
给你一个正整数
n
,生成一个包含1
到n2
所有元素,且元素按顺时针顺序螺旋排列的n x n
正方形矩阵matrix
。
class Solution {
public:
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 = 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++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
vector<vector<int>> res(n, vector<int>(n, 0))是创建了一个n行n列,全为0的二维数组
// 将二维数组v,初始化为m行n列,全为0,写法如下:
vector<vector<int>> v(m, vector<int>(n,0));
错误写法:为什么?
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n,vector<int>(n,0));
int loop=n/2;
int startx=0;
int starty=0;
int count=1;
int mid=n/2;
int offset=1;
int i,j;
while(loop--)
{
i=startx;
j=starty;
for(int j=starty;j<n-offset;j++)//for循环里写int j就不对
{
res[startx][j]=count;
count++;
}
for(int i=startx;i<n-offset;i++)//for循环里写int i就不对
{
res[i][j]=count;
count++;
}
for(;j>starty;j--)
{
res[i][j]=count;
count++;
}
for(;i>startx;i--)
{
res[i][j]=count;
count++;
}
startx++;
starty++;
offset++;
}
if(n%2==1)
{res[mid][mid]=count;}
return res;
}
};
Python:
想要构建一个m行n列的二维数组
L = [[ ] * n] * m
如果想要给一个初始化的值,可以写成:
L = [[0] * n] * m
但是,由于此方法构造的二维数组是对[]*n引用了m次,更改其中一行的值会导致每行的值都被更改。所以,并不是一个可以完全操控到列粒度的二维数组
注意:区分nums = [[0] * n] * n和nums = [[0] * n for _ in range(n)]
nums = [[0] * n for _ in range(n)]
这行代码创建了一个二维列表 nums,其中包含 n 行和 n 列,并且每个元素都被初始化为 0。
让我们逐步解释这行代码:
range(n) 创建一个包含从 0 到 n-1 的整数序列。在这个例子中,它用于控制列表的行数和列数。
for _ in range(n) 使用 _(一个通常用于表示不需要使用的变量的惯用方法)迭代整数序列,即对每一行进行迭代。
[0] * n 创建一个包含 n 个零的列表,即一行的元素。
整个表达式 [[0] * n for _ in range(n)] 使用列表推导式,通过在每一行都复制 [0] * n,最终创建了一个 n x n 的二维列表,其中每个元素都是 0。
这种方式是在初始化矩阵或二维数组时的一种简洁的方法,特别是在 Python 中,可以避免常见的嵌套列表问题。
nums = [[0] * n] * n
nums = [[0] * n] * n 创建了一个包含 n 行的二维列表,其中每一行都是对相同的 [0] * n 列表的引用。这意味着如果修改其中一个行的值,所有行都会受到影响,因为它们引用的是同一个列表对象
n = 3 nums = [[0] * n] * n nums[0][0] = 1 print(nums) # 输出: # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
相比之下,nums = [[0] * n for _ in range(n)] 使用列表推导式为每一行都创建了一个新的 [0] * n 列表。这样,每一行都是独立的,修改一行不会影响其他行 。
n = 3 nums = [[0] * n for _ in range(n)] nums[0][0] = 1 print(nums) # 输出: # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
class Solution(object):
def generateMatrix(self, n):
startx=0
starty=0
loop=n//2
mid=n//2
nums=[[0]*n for _ in range(n)]
count=1
for offset in range(1,loop+1):
for i in range(startx,n-offset):
nums[startx][i]=count
count+=1
for i in range(starty,n-offset):
nums[i][n-offset]=count
count+=1
for i in range(n-offset,starty,-1):
nums[n-offset][i]=count
count+=1
for i in range(n-offset,startx,-1):
nums[i][starty]=count
count+=1
startx+=1
starty+=1
if n%2!=0:
nums[mid][mid]=count
return nums