【算法笔记】不用库函数手撕力扣之力扣15:三数之和

题目链接

题解

方法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%的用户
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值