977.有序数组的平方
给定一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
暴力排序
每个数平方后排序
这里有两步操作--平方操作和冒泡排序,平方操作的时间复杂度为 O(n),冒泡排序的时间复杂度为 O(n^2),整个函数的时间复杂度为 O(n^2)。
(冒泡排序需要进行 numsSize - 1 轮比较和交换操作,每轮操作需要比较 numsSize - i - 1 对相邻元素,并可能进行交换。在最坏情况下,需要进行 n-1+n-2+...+1 = (n-1)*n/2 次比较和交换。)
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* sortedSquares(int* nums, int numsSize, int* returnSize) {
int* result = (int*)malloc(numsSize * sizeof(int)); // 分配结果数组的内存
for (int i = 0; i < numsSize; i++) {
result[i] = nums[i] * nums[i];
}
// 使用冒泡排序对平方后的数组进行非递减排序
for (int i = 0; i < numsSize - 1; i++) {
for (int j = 0; j < numsSize - i - 1; j++) {
if (result[j] > result[j + 1]) {
int temp = result[j];
result[j] = result[j + 1];
result[j + 1] = temp;
}
}
}
*returnSize = numsSize; // 将结果数组的大小通过指针参数返回
return result; // 返回结果数组的指针
}
双指针法
数组为有序数组, 但负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
i指向左边,j指向右边。A[i] * A[i] 和A[j] * A[j]进行大小比较,存放在result[k]中。
时间复杂度为O(n)
int* sortedSquares(int* nums, int numsSize, int* returnSize){
*returnSize = numsSize; //返回的数组大小就是原数组大小
int j = numsSize - 1;//右
int i = 0;//左
int* result = (int*)malloc(sizeof(int) * numsSize);
int k;
for(k = numsSize - 1; k >= 0; k--) {
if(nums[i] * nums[i] > nums[j] * nums[j]) {
result[k] = nums[i] * nums[i];
i++;
} //若左指针指向元素平方比右指针指向元素平方大,将左指针指向元素平方放入结果数组。左指针右移一位
else {
result[k] =nums[j] * nums[j];
j--;
} //若右指针指向元素平方比左指针指向元素平方大,将右指针指向元素平方放入结果数组。右指针左移一位
}
return result;
}
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
(我们要找到一个连续子数组,使得从数组中取出该子数组的元素并求和,得到的结果大于等于给定的正整数 s,并且该子数组的长度要尽可能地小。
例如,假设给定数组为 [2, 3, 1, 2, 4, 3],目标正整数为 7。我们可以找到多个满足条件的子数组,如 [2, 3, 1, 2]、[3, 1, 2, 4] 等,但最短的子数组是 [4, 3],其元素之和为 7,因此返回长度 2。)
暴力解法
两层for循环,时间复杂度O(n^2)
内层for循环从起始位置开始,逐个加上后续元素的值,直到总和大于等于给定的正整数 s 或者遍历到数组末尾,如果找到了满足条件的子数组,我们记录下其长度。外层for循环依次向后遍历继续寻找其他满足条件的子数组,并更新最短子数组的长度。最后返回最短子数组的长度。
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
int minSubArrayLen(int target, int* nums, int numsSize) {
int minLength = INT_MAX; // 初始化最短子数组长度为最大值,INT_MAX 是 C 语言中 <limits.h> 头文件中定义的一个常量,表示 int 类型的最大值。
for (int i = 0; i < numsSize; i++) { // 枚举子数组的起始位置
int sum = 0; // 记录子数组元素之和
for (int j = i; j < numsSize; j++) { // 从起始位置开始累加元素
sum = sum + nums[j];
if (sum >= target) { // 如果累加和大于等于s,则更新最短子数组长度
minLength = fmin(minLength, j - i + 1);//fmin() 是 C 语言中的一个数学函数,它的作用是返回两个数中较小的那个数。
/* if (j - i + 1 < minLength) {
minLength = j - i + 1;
}*/
break;
}
}
}
return (minLength == INT_MAX) ? 0 : minLength; // 如果没有找到满足条件的子数组,则返回0
}
滑动窗口
不断的调节子序列的起始位置和终止位置。
i不变,j不断像后移,如果当前窗口的总值大于s了,i就向后移动。(j是遍历数组的指针,也就是for循环里的索引)
- 时间复杂度:O(n)
- 空间复杂度:O(1)
(虽然for循环里嵌套while,但时间复杂度主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n) 。暴力解法n × n)
int minSubArrayLen(int target, int* nums, int numsSize){
//初始化最小长度为INT_MAX
int minLength = INT_MAX;
int sum = 0;
int i = 0, j = 0;
//右边界向右扩展
for(; j < numsSize; j++) {
sum += nums[j];
//当sum的值大于等于target时,保存长度,并且收缩左边界
while(sum >= target) {
int newLength = j - i + 1;
minLength = minLength <newLength ? minLength : newLength ;
sum -= nums[i++];
}
}
//若minLength不为INT_MAX,则返回minLnegth
return minLength == INT_MAX ? 0 : minLength;
}
59.螺旋矩阵II
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
顺时针画矩阵的过程:每一圈上行从左到右,右列从上到下,下行从右到左,左列从下到上填充。
定义变量 startX
和 startY
表示每次循环的起始位置,loop
表示循环圈数,offset
偏移值用于控制每一圈的边界向内收缩的单位数,count
表示当前要添加的元素,mid
表示二维数组的中心位置(如果 n
是奇数的话)。
将 count
的值逐个赋给相应的位置。每一条边都要左闭右开。offset
每次增加 2,startX
和 startY
每次增加 1,loop
减少 1。循环的圈数loop为n / 2,如果n为奇数,需要最后在中间填入最后一个数字的值。
- 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
- 空间复杂度 O(1)
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
//初始化返回的结果数组的大小
*returnSize = n;
*returnColumnSizes = (int*)malloc(sizeof(int) * n);
//初始化返回结果数组ans
int** ans = (int**)malloc(sizeof(int*) * n);
int i;
for(i = 0; i < n; i++) {
ans[i] = (int*)malloc(sizeof(int) * n);
(*returnColumnSizes)[i] = n;
}
//设置每次循环的起始位置
int startX = 0;
int startY = 0;
//循环圈数
int loop = n / 2;
//设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
int mid = n / 2;
//偏移数,每个圈的边界最开始时是向内缩进 1 个单位
int offset = 1;
//当前要添加的元素
int count = 1;
while(loop) {
int i = startX;
int j = startY;
//模拟上侧从左到右
for(; j < startY + n - offset; j++) {
ans[startX][j] = count++;
}
//模拟右侧从上到下
for(; i < startX + n - offset; i++) {
ans[i][j] = count++;
}
//模拟下侧从右到左
for(; j > startY; j--) {
ans[i][j] = count++;
}
//模拟左侧从下到上
for(; i > startX; i--) {
ans[i][j] = count++;
}
//偏移值每次加2(每一圈从左到右和从上到下的填充都会占据两个边界的位置)
offset+=2;
//遍历起始位置每次+1
startX++;
startY++;
loop--;
}
//若n为奇数需要单独给矩阵中间赋值
if(n%2)
ans[mid][mid] = count;
return ans;
}