完成初级算法用了将近半年的时间,而且。。。已经忘得差不多了。。。。。
❤ 2022.2.23 ❤
今天的题目是:
三数之和
我的思路:
既然是三数之和,那应该就是先按顺序把两个数相加,再检索后面有没有与其和为0的数呗。
返回值要求是个二维数组,虽然说现在我对c语言动态分配二维数组很熟练了(真的!),但是还是感觉好麻烦,在考虑要不要改成c++或者python。。。其实一直也想用python可是懒。。。
一开始我是这样做的
先把输入数组进行排序,然后因为题目要求不能有重复的三元组,所以我在实现过程中过滤掉那些重复的元素,最后找到答案
int cmp(const void* _a, const void* _b)
{
int a = *(int*)_a, b = *(int*)_b;
return a - b;
}
/**
* 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** threeSum1(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 0;
int rear = 0;
int n = 0;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * (numsSize / 3 + 1));
int** result = (int**)malloc(sizeof(int*) * (numsSize / 3 + 1));
qsort(nums, numsSize, sizeof(int), cmp);
while (front < numsSize - 2)
{
while (front < numsSize - 2 && nums[front] == nums[front + 1])
{
front++;
rear = front + 1;
}
while (rear < numsSize - 1 && nums[rear] == nums[rear + 1])
{
rear++;
n = rear + 1;
}
while (n < numsSize && nums[n] == nums[n + 1])
n++;
if (nums[front] + nums[rear] + nums[n] == 0)
{
(*returnSize)++;
(*returnColumnSizes)[*returnSize - 1] = 3;
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[front];
result[*returnSize - 1][1] = nums[rear];
result[*returnSize - 1][2] = nums[n];
rear++;
n = rear + 1;
}
else
n++;
if (n == numsSize)
{
rear++;
n = rear + 1;
}
if (rear == numsSize - 1)
{
front++;
rear = front + 1;
n = rear + 1;
}
}
return result;
}
可是这样是有问题的,因为过滤掉了重复元素,所以类似[-1 -1 2]这样的答案就被过滤掉了,而且可能因为我在while中用了可能导致越界的语句,所以leetcode的编译器不给我通过。。。
那就改改吧,从去重重复元素到去除重复答案。
int cmp(const void* _a, const void* _b)
{
int a = *(int*)_a, b = *(int*)_b;
return a - b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 0;
int rear = 1;
int n = 2;
bool newArray = true;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int));
int** result = (int**)malloc(sizeof(int*)); //注意:结果可能有各种组合,数量可能比numsSize还多,只能动态分配。。。
qsort(nums, numsSize, sizeof(int), cmp);
while (front < numsSize - 2)
{
if (nums[front] + nums[rear] + nums[n] == 0)
{
for (int i = 0; i < *returnSize; i++)
{
if (nums[front] == result[i][0])
{
if (nums[rear] == result[i][1])
{
newArray = false;
n++;
}
}
}
if (newArray == true)
{
(*returnSize)++;
*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * (*returnSize));
(*returnColumnSizes)[*returnSize - 1] = 3; //注意加小括号,否则优先和[]结合
result = (int**)realloc(result, sizeof(int*) * (*returnSize));
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[front];
result[*returnSize - 1][1] = nums[rear];
result[*returnSize - 1][2] = nums[n];
n++;
}
newArray = true;
}
else
n++;
if (n == numsSize)
{
rear++;
n = rear + 1;
}
if (rear == numsSize - 1)
{
front++;
rear = front + 1;
n = rear + 1;
}
}
return result;
}
但是。。。不出意料的。。。超时了。。。果然暴力破解不行啊
不过我在调试过程中还是遇到了一些问题,比如关于动态二维数组的内存分配
这里我还是什么时候该加*,什么时候该加括号记得不是太清,而且这里无法确定结果有多少组,可能比输入数组长度还长,所以动态分配比较好。
先看看答案吧
看了答案之后吧,我觉得。。。好想跟我的思路差不多,但是我的程序就是通不过呀。。。
于是我参照答案修改了下我的答案。
首先是遍历输入数组,然后在比其小的部分和比其大的部分里寻找和他和为0的元素。
去重策略是在遍历过程中遇到相同元素时,再去寻找结果数组中是否有和当前组合相同的结果,如果有则跳过,没有则增加。
调了半天结果应该没问题,但是依然超时。。。
那么我的代码到底差在哪里呢。。。
思考了一下,可能问题出在去重那里。
我的代码思路是在比目标元素小的和大的区间里寻找和为0的元素,所以遇到相同的元素时不能直接跳过,而是要得到结果之后再判断,确定至少两个结果都为重复时再跳过。但是按照答案的思路,每个目标元素的权重都是相同的,于是遇到相同元素时可以直接跳过。
那么我来改一下代码。
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 1;
int rear = numsSize - 1;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int));
int** result = (int**)malloc(sizeof(int*)); //注意:结果可能有各种组合,数量可能比numsSize还多,只能动态分配。。。
qsort(nums, numsSize, sizeof(int), cmp);
for (int i = 0; i < numsSize; i++)
{
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > 0)
break;
front = i + 1;
rear = numsSize - 1;
while (front < rear)
{
if (nums[i] + nums[front] + nums[rear] > 0)
{
rear--;
continue;
}
else if (nums[i] + nums[front] + nums[rear] < 0)
{
front++;
continue;
}
else
{
(*returnSize)++;
*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * (*returnSize));
(*returnColumnSizes)[*returnSize - 1] = 3; //注意加小括号,否则优先和[]结合
result = (int**)realloc(result, sizeof(int*) * (*returnSize));
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[i];
result[*returnSize - 1][1] = nums[front];
result[*returnSize - 1][2] = nums[rear];
front++;
rear--;
}
while (front < rear && nums[front] == nums[front - 1])
front++;
while (front < rear && nums[rear] == nums[rear + 1])
rear--;
}
}
return result;
}
这段代码和答案的思路是一样的,就是依次遍历数组,然后在所遍历元素的后面寻找与其和为零的元素。其中左指针从所遍历元素的下一个开始,右指针从最后一个开始,到所遍历元素大于零或左指针大于右指针为止。
关于去重我开始用的是这样的形式
首先遍历数组元素的去重就是在遇到重复元素时跳过,但是在寻找和为零的两个元素时这样就不行了,如果依然在开头判断和前一个元素相同就跳过的话,会造成左指针指向的第一个元素和所遍历的数组元素相同时左指针所在元素被跳过,所以这里的遍历应该放在至少执行过一次和为零的判断之后再执行去重操作。
而在while循环的结尾去重时就不能直接用continue了,这样每判断一次就会执行一次判断和为零的操作,可能导致重复结果。所以需要用while循环找到不重复的指针项再继续循环。
但是非常尴尬的是,提交依然超时了。。。
我T****。。。
那没办法了,我又去看了别人用c语言提交的答案,然后继续改。。。
我看了看别人的答案,发现和我的不一样的地方大概就是我是每次增加返回结果的时候都用realloc函数动态分配数组,而别人直接在开始的时候就分配了足够的数组(n*n)个,于是我也改成了这样的
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 1;
int rear = numsSize - 1;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * numsSize * numsSize);
int** result = (int**)malloc(sizeof(int*) * numsSize * numsSize); //注意:结果可能有各种组合,数量可能比numsSize还多,只能动态分配。。。
qsort(nums, numsSize, sizeof(int), cmp);
for (int i = 0; i < numsSize; i++)
{
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > 0)
break;
front = i + 1;
rear = numsSize - 1;
while (front < rear)
{
if (nums[i] + nums[front] + nums[rear] > 0)
{
rear--;
continue;
}
else if (nums[i] + nums[front] + nums[rear] < 0)
{
front++;
continue;
}
else
{
(*returnSize)++;
//*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * (*returnSize));
(*returnColumnSizes)[*returnSize - 1] = 3; //注意加小括号,否则优先和[]结合
//result = (int**)realloc(result, sizeof(int*) * (*returnSize));
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[i];
result[*returnSize - 1][1] = nums[front];
result[*returnSize - 1][2] = nums[rear];
front++;
rear--;
}
while (front < rear && nums[front] == nums[front - 1])
front++;
while (front < rear && nums[rear] == nums[rear + 1])
rear--;
}
}
return result;
}
我去终于通过了,这道题写了快一个月了,难道这就是中级算法的力量吗?!
那么问题来了,为什么答案用n*n的内存就可以装下呢?
算了,以后再说吧。。。我累了。。。
所以说,这个题如果我用的是c++的话,可能早就搞定了吧。。。
于是我决定练习一下c++
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++)
{
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > 0)
break;
int front = i + 1;
int rear = nums.size() - 1;
while (front < rear)
{
if (nums[i] + nums[front] + nums[rear] > 0)
{
rear--;
continue;
}
else if (nums[i] + nums[front] + nums[rear] < 0)
{
front++;
continue;
}
else
{
result.push_back({ nums[i], nums[front], nums[rear] });
front++;
rear--;
}
while (front < rear && nums[front] == nums[front - 1])
front++;
while (front < rear && nums[rear] == nums[rear + 1])
rear--;
}
}
return result;
}
};
果然so easy
顺便回忆了一下sort()函数和vector二维数组的使用。