题解
方法1:排序+求和逼近
主要思路
首先对数组进行从小到的排序,从小到大遍历每个数据。在每个数据遍历过程中,要从这个数据右边的所有数据中,找两个数使得这两个数的和与遍历的数据互为相反数,采用从两边向中间求和逼近的方法可以使得每次遍历复杂度降到O(n)。
源代码:c
#include <stdio.h>
#include <malloc.h>
#include <string.h>
void fastSort(int* data, int startIndex, int endIndex); //快排
int** coupleInsert(int** p, int *cap, int *len, int *num); //一个只能插入的动态数组
/**
* 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** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
{
if(numsSize < 3)
{ //少于三个数,直接返回
*returnSize = 0;
return NULL;
}
*returnSize = 0;
int **result;
int len = 0; //数组中实际存储数据的长度
int cap = 0; //数组容量
//给数组排序
fastSort(nums, 0, numsSize - 1);
//遍历每个数据,并寻找与之加和为0的两个数据
for(int i = 0; i < numsSize; i++)
{
if(nums[i] > 0)
break;
if(i > 0 && nums[i] == nums[i - 1])
//去除重复数据
continue;
int rightIndex = numsSize - 1;
int leftIndex = i + 1;
while(leftIndex < rightIndex)
{
// 两边向中间逼近寻找加和为0的两个数
if(nums[i] + nums[leftIndex] + nums[rightIndex] < 0)
{ //小于0,leftIndex右移使结果增大
leftIndex++;
while(leftIndex < rightIndex && nums[leftIndex] == nums[leftIndex - 1])
leftIndex++;
}
else if(nums[i] + nums[leftIndex] + nums[rightIndex] == 0)
{ //相等,记录数据 leftIndex rightIndex同时向中间移动一下,寻找下一组解
int threeNums[3] = {nums[i], nums[leftIndex], nums[rightIndex]};
result = coupleInsert(result, &cap, &len, threeNums);
// leftIndex rightIndex同时向中间移动一下,寻找下一组解
leftIndex++;
while(leftIndex < rightIndex && nums[leftIndex] == nums[leftIndex - 1])
leftIndex++;
rightIndex--;
while(leftIndex < rightIndex && nums[rightIndex] == nums[rightIndex + 1])
rightIndex--;
}
else
{ //大于0,rightIndex左移,使结果减小
rightIndex--;
while(leftIndex < rightIndex && nums[rightIndex] == nums[rightIndex + 1])
rightIndex--;
}
}
}
*returnSize = len;
*returnColumnSizes = (int*)malloc(len*sizeof(int));
for(int i = 0; i < len; i++)
(*returnColumnSizes)[i] = 3;
return result;
}
//实现一个只能插入数据的动态数组
int** coupleInsert(int** p, int *cap, int *len, int *num)
{
if(*cap == 0)
{
*cap = 10;
p = (int**)malloc(sizeof(int*)*10);
}
else if(*cap == *len)
{
int** tempP = (int**)malloc(sizeof(int*)*(*cap)*2);
*cap = (*cap)*2;
for(int i = 0; i < *len; i++)
{
//开辟新数组缓存
tempP[i] = (int*)malloc(sizeof(int)*3);
tempP[i][0] = p[i][0];
tempP[i][1] = p[i][1];
tempP[i][2] = p[i][2];
//释放原数组缓存
free(p[i]);
}
//释放原数组缓存
free(p);
p = tempP;
}
//插入新数据
p[*len] = (int*)malloc(3*sizeof(int));
p[*len][0] = num[0];
p[*len][1] = num[1];
p[*len][2] = num[2];
(*len)++;
return p;
}
// 快排
void fastSort(int* data, int startIndex, int endIndex)
{
if (endIndex == startIndex)
return;
int tempIndex = (startIndex + endIndex) / 2;
int temp = data[tempIndex];
data[tempIndex] = data[endIndex];
data[endIndex] = temp;
int midIndex = startIndex - 1; //midIndex左边的数据都是比最后一个数据小的数
for (int i = startIndex; i < endIndex; i++)
{
if (data[i] < data[endIndex])
{
midIndex++;
int temp = data[i];
data[i] = data[midIndex];
data[midIndex] = temp;
}
}
midIndex++;
temp = data[midIndex];
data[midIndex] = data[endIndex];
data[endIndex] = temp;
if(midIndex - startIndex > 1)
{
fastSort(data, startIndex, midIndex - 1);
}
if (endIndex - midIndex > 1)
{
fastSort(data, midIndex + 1, endIndex);
}
}
时空复杂度
- 用时:144 ms, 在所有 C 提交中击败了41.37%的用户
- 内存:21.1 MB, 在所有 C 提交中击败了80.75%的用户
中途错误记录
- 对于数组中的重复数据没有进行特殊处理,在每次遍历及leftIndex/rightIndex移动时,检查与上个数据是否相等,相等就跳过得到解决。相关代码如下:
if(i > 0 && nums[i] == nums[i - 1])
//去除重复数据
continue;
while(leftIndex < rightIndex && nums[leftIndex] == nums[leftIndex - 1])
leftIndex++;
while(leftIndex < rightIndex && nums[rightIndex] == nums[rightIndex + 1])
rightIndex--;
方法2:排序+正负数据分类处理
主要思路
对数据进行排序,随后分成正负两类,根据三个数据可能组合为:负负正,正负负两种情况使用类似逼近的方法进行处理。这种方法处理起来比较麻烦,要对0数据进行特殊处理,同时在复杂度上也没有优势。
源代码:C
#include <stdio.h>
#include <malloc.h>
#include <string.h>
//实现一个只能插入的动态数组
//typedef struct
//{
// int num1;
// int num2;
// int num3;
//} Couple;
//
//typedef struct
//{
// Couple *p;
// int capacity;
// int length;
//} CoupleVec;
void fastSort(int* data, int startIndex, int endIndex);
//Couple* coupleInsert(Couple* vec, int* length, int *capacity, Couple theC);
int** coupleInsert(int** p, int *cap, int *len, int *num);
/**
* 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** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
// {
// *returnSize = 0;
// return NULL;
// }
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
{
if(numsSize < 3)
{
*returnSize = 0;
return NULL;
}
*returnSize = 0;
int **result = NULL;
int len = 0;
int cap = 0;
//给数组排序
fastSort(nums, 0, numsSize - 1);
// 找到正负数的分界点
int posStartIndex = -1;
int couple;
for(int i = 0; i < numsSize; i++)
{
if(nums[i] >= 0)
{
posStartIndex = i;
if(i < numsSize - 2 && nums[i] == 0 && nums[i + 1] == 0 && nums[i + 2] == 0)
{
int threeNums[3] = {0, 0, 0};
result = coupleInsert(result, &cap, &len, threeNums);
}
break;
}
}
// 没有正数或负数,直接返回
if(posStartIndex == -1 || posStartIndex == 0 || posStartIndex >= numsSize)
{
*returnSize = len;
*returnColumnSizes = (int*)malloc(len*sizeof(int));
for(int i = 0; i < len; i++)
(*returnColumnSizes)[i] = 3;
return result;
}
// 选两个负数 一个正数
for(int i = 0; i < posStartIndex; i++)
{
if(i > 0 && nums[i] == nums[i - 1])
continue;
int posIndex = numsSize - 1;
for(int j = i + 1; j < posStartIndex; j++)
{
int negSum = nums[i] + nums[j];
while(posIndex >= posStartIndex && negSum + nums[posIndex] > 0)
{ //切换到下一个不同的值
posIndex--;
}
if(posIndex < posStartIndex)
break;
if(negSum + nums[posIndex] == 0)
{
int threeNums[3] = {nums[i], nums[j], nums[posIndex]};
result = coupleInsert(result, &cap, &len, threeNums);
posIndex = posIndex - 1;
while((posIndex) >= posStartIndex && nums[posIndex] == nums[posIndex + 1])
posIndex = posIndex - 1;
}
}
}
// 选两个正数 一个负数
for(int i = posStartIndex; i < numsSize; i++)
{
if(i > posStartIndex && nums[i] == nums[i - 1])
continue;
int negIndex = posStartIndex - 1;
for(int j = i + 1; j < numsSize; j++)
{
int posSum = nums[i] + nums[j];
while(negIndex >= 0 && posSum + nums[negIndex] > 0)
{
negIndex--;
}
if(negIndex < 0)
break;
if(posSum + nums[negIndex] == 0)
{
int threeNums[3] = {nums[negIndex], nums[i], nums[j]};
result = coupleInsert(result, &cap, &len, threeNums);
negIndex = negIndex - 1;
while(negIndex >= 0 && nums[negIndex] == nums[negIndex + 1])
negIndex = negIndex - 1;
}
}
}
*returnSize = len;
*returnColumnSizes = (int*)malloc(len*sizeof(int));
for(int i = 0; i < len; i++)
(*returnColumnSizes)[i] = 3;
return result;
}
int** coupleInsert(int** p, int *cap, int *len, int *num)
{
if(*cap == 0)
{
*cap = 10;
p = (int**)malloc(sizeof(int*)*10);
}
else if(*cap == *len)
{
int** tempP = (int**)malloc(sizeof(int*)*(*cap)*2);
*cap = (*cap)*2;
for(int i = 0; i < *len; i++)
{
//开辟新数组缓存
tempP[i] = (int*)malloc(sizeof(int)*3);
tempP[i][0] = p[i][0];
tempP[i][1] = p[i][1];
tempP[i][2] = p[i][2];
//释放原数组缓存
free(p[i]);
}
//释放原数组缓存
free(p);
p = tempP;
}
//插入新数据
p[*len] = (int*)malloc(3*sizeof(int));
p[*len][0] = num[0];
p[*len][1] = num[1];
p[*len][2] = num[2];
(*len)++;
return p;
}
// 快排
void fastSort(int* data, int startIndex, int endIndex)
{
if (endIndex == startIndex)
return;
int tempIndex = (startIndex + endIndex) / 2;
int temp = data[tempIndex];
data[tempIndex] = data[endIndex];
data[endIndex] = temp;
int midIndex = startIndex - 1; //midIndex左边的数据都是比最后一个数据小的数
for (int i = startIndex; i < endIndex; i++)
{
if (data[i] < data[endIndex])
{
midIndex++;
int temp = data[i];
data[i] = data[midIndex];
data[midIndex] = temp;
}
}
midIndex++;
temp = data[midIndex];
data[midIndex] = data[endIndex];
data[endIndex] = temp;
if(midIndex - startIndex > 1)
{
fastSort(data, startIndex, midIndex - 1);
}
if (endIndex - midIndex > 1)
{
fastSort(data, midIndex + 1, endIndex);
}
}
时空复杂度
- 用时:144 ms, 在所有 C 提交中击败了41.37%的用户
- 内存:21.1 MB, 在所有 C 提交中击败了80.75%的用户