力扣977
视频讲解
法一:暴力&快排
主要思路:先把原数组所有元素依次平方,再对平方后数组快排
易错点:
无
代码实现:
int compare(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int* sortedSquares(int* nums, int numsSize, int* returnSize){
*returnSize = numsSize;
int* ans = (int* )malloc(sizeof(int) * numsSize);
for(int i = 0; i < numsSize; i++) {
ans[i] = nums[i] * nums[i];
}
qsort(ans, numsSize, sizeof(int),compare);
return ans;
free(ans);
}
第一次写错误
不熟悉快排,而且貌似力扣不能自己额外定义函数
补充
1 快速排序
快速排序是一种基于比较的排序算法,其核心思想是通过将待排序序列不断地分割为较小的子序列并对每个子序列进行排序来实现整个序列的排序。具体过程如下:
选取一个基准元素(pivot),一般为序列中的一个元素,将序列中小于等于基准元素的数放在基准元素左边,大于基准元素的数放在右边。此时,基准元素所处的位置即为其最终排序后的位置。
递归地对左右两个子序列进行排序,直到整个序列有序。
下面是使用C语言实现快速排序的代码:
void quick_sort(int arr[], int left, int right) {
int i = left, j = right;
int pivot = arr[(left + right) / 2]; //注意pivot必须这样写,不能写成pivot = (left + right) / 2, 然后在下面的比较条件中写arr[pivot]
while (i <= j) {
while (arr[i] < pivot) {
i++;
}
while (arr[j] > pivot) {
j--;
}
if (i <= j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
if (left < j) {
quick_sort(arr, left, j);
}
if (i < right) {
quick_sort(arr, i, right);
}
}
详细解释
arr
是待排序的数组;left
是数组的左边界(起始下标);right
是数组的右边界(终止下标)。快速排序算法的核心思想是通过递归地将数组分割成小的子数组,并且对每个子数组进行排序,最终实现对整个数组的排序。
具体实现如下:
首先,算法需要选择一个基准元素,一般选择数组中的一个元素,称为枢轴(pivot)。
然后,将数组中所有小于等于枢轴的元素都移到枢轴的左侧,将大于枢轴的元素都移到枢轴的右侧。这个过程称为划分(partition)。
接着,递归地对枢轴左侧和右侧的子数组进行排序。递归结束的条件是子数组的长度小于等于1。
代码实现中,首先选择中间的元素作为枢轴,计算出它的下标为(left + right) / 2
,然后进行划分操作。具体来说,先将左指针i
向右移动,直到找到一个大于等于枢轴的元素;再将右指针j
向左移动,直到找到一个小于等于枢轴的元素。如果此时i <= j
,则交换arr[i]
和arr[j]
的值,并将i
和j
分别向右和向左移动一位。重复执行这个过程,直到i > j
,即左右指针相遇。
最后,如果枢轴左侧的子数组长度大于1,则递归调用quick_sort
函数对左侧子数组进行排序。同理,如果枢轴右侧的子数组长度大于1,则递归调用quick_sort
函数对右侧子数组进行排序。
2 c语言当需要对一个整型数组进行排序时,可以使用 qsort
函数进行快速排序,其函数原型为
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
其中,参数含义如下:
base
:待排序数组的首元素地址。nmemb
:待排序数组的元素个数。size
:待排序数组的元素大小(字节数)。compar
:比较函数的指针,用于指定元素的比较方法。该函数需要接收两个 const void *
类型的指针作为参数,返回值为整数类型,表示两个元素的大小关系。这四个参数一个也不能少,由于 qsort
函数需要使用指针类型,因此它可以用于对任意类型的数组进行排序,包括基本类型、结构体、指针等。在使用时,我们需要根据实际情况编写相应的比较函数compare,以便 qsort
函数能够正确地排序数组中的元素。
以本题为例解释compare函数写法
int compare(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
这个函数首先将 a
和 b
强制转换为 int
类型的指针,然后通过解引用操作符 *
访问到指针所指向的整型变量,并计算它们的差值,将结果作为比较函数的返回值。这里返回值的意义是,如果 a
比 b
小,返回一个负数,如果 a
和 b
相等,返回 0,如果 a
比 b
大,返回一个正数。
需要注意的是,在使用 qsort
函数时,比较函数的返回值必须满足如下约定:
a
和 b
的类型相同,那么返回值必须是该类型的整数差值。a
和 b
的类型不同,那么返回值必须是一种可转换为 int
类型的整数类型,如 long
或 ptrdiff_t
。法二:双指针
主要思路:
(1) 因为给出的数组是非递减排序,所以平方后的结果是从两边到中间非递增排序
(2) 利用两个下标遍历原数组,再用一个下标创建新数组
(3) 把每次遍历原数组中平方较大的一方赋给新数组
易错点:
(1)遍历原数组的条件定为i<=j,若是i 代码实现: 第一次写错误 (1) 循环条件应该是 左 <= 右 (2)创建新数组应该是从后往前放元素 补充 无 力扣209 视频讲解 主要思路: 利用“变形”的双指针,先移动终止位置的指针,找出以0到此位置时所有元素求和大于目标值的情况,再固定终止位置的指针,从0开始移动初始位置的指针直到两个指针之间元素和小于目标值 易错点: (1) 先固定的是终止位置的指针 (2)有可能所有元素的和都没目标值大 代码实现: 第一次写错误 没看清题目,忽视了可能所有元素的和都没目标值大这种情况 力扣59 视频讲解 主要思路: 逆时针填数 易错点: (1)区间不变性,每圈的每一条边都是左闭右开区间,即最后一位不填 (2)因为是按圈数限制,所以n若为奇数则中间会空一个 (3)offset的意义在于 结束一圈后 起始位置向后移 结束位置向前移 代码实现: 第一次写错误 无 补充 二级指针 例如, 需要注意的是,二维数组并不是指针数组, (2)int** returnColumnSizes ;(*returnColumnSizes)[i] = n 在 int* sortedSquares(int* nums, int numsSize, int* returnSize){
int j, k, i;
i = 0;
j = k = numsSize - 1;
*returnSize = numsSize;
int* ans = (int* )malloc(sizeof(int) * numsSize);
while(i <= j) {
if(pow(nums [i], 2) < pow(nums[j], 2)) {
ans[k--] = nums[j] * nums[j];
j--;
}
else {
ans[k--] = nums[i] * nums[i];
i++;
}
}
return ans;
free(ans);
}
int minSubArrayLen(int target, int* nums, int numsSize) {
int i = 0, sum = 0, ret = numsSize+1, len = 0; //先将ret定为比numSize大1,而不需要定义为1e5
for (int j = 0; j < numsSize; j++) { //移动终止位置指针
sum += nums[j];
while (sum >= target) { //移动起始位置指针
len = j - i + 1; //在循环中计算每次可能的长度,如果出循环判断就要写为len = j - (i - 1) + 1
if (len < ret) {
ret = len;
}
sum -= nums[i++];
}
}
return ret == numsSize+1 ? 0 : ret; //用三目元算符返回结果
}
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;
//设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
int mid = n / 2;
//循环圈数
int loop = n / 2;
//偏移数
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;
}
(1)int **
是指一个指向指针的指针,也被称为二级指针。它可以被用来表示指向指针数组的指针,或者表示一个二维数组。int **arr
定义了一个指向指针的指针,这个指针可以指向一个 int
类型的指针数组,也可以指向一个二维数组。在指向指针数组的情况下,arr[i]
代表第 i
个指针,而 arr[i][j]
代表第 i
个指针所指向的 int
数组的第 j
个元素。int **
定义的指针不能直接指向二维数组。二维数组是由多个一维数组组成的,因此它的内存布局与指针数组是不同的。如果要使用 int **
指针指向二维数组,需要先在堆上分配足够的空间,并逐个设置指向一维数组的指针。int** returnColumnSizes
定义了一个二级指针,它指向一个数组,这个数组里的每个元素都是 int*
类型的指针。(*returnColumnSizes)[i] = n
中,(*returnColumnSizes)
用来取得指针 returnColumnSizes
指向的数组的指针,即 int*
类型的指针。(*returnColumnSizes)[i]
就是数组中第 i
个元素的指针,它指向一个整型变量的地址,n
赋值给这个整型变量,表示第 i
列的元素个数为 n
。