leetcode 15 3Sum

3Sum
Total Accepted: 70783 Total Submissions: 419673

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.

For example, given array S = {-1 0 1 2 -1 -4},

A solution set is:
(-1, 0, 1)
(-1, -1, 2)

这道题是之前的第一题2Sum的升级版,除了要求找出所有可能的符合的3元组之外,还要以非降序输出。下面就结合代码来解释:

int** threeSum(int* nums, int numsSize, int* returnSize)
{
    if(numsSize == 0)
        return NULL;
    int min_element = 100000000;
    int max_element = -100000000;
    for(int i = 0; i < numsSize; i++)
    {
        if(min_element > nums[i])
            min_element = nums[i];
        if(max_element < nums[i])
            max_element = nums[i];
    }

    int offset = -1* min_element;

函数一开始先判断是空的情况,如果是空则返回NULL。然后找出整个输入数据中的最大值和最小值,这样的目的是为了确定hash表的长度以及偏移量offset的值。偏移量等于最小元素值的相反数,这样的目的是为了通过offset使得min_element对应于hash表的第一个位置。

    int *hashTable = (int*)malloc(sizeof(int)*(max_element - min_element + 1));

    memset(hashTable, 0, sizeof(int) * (max_element - min_element + 1));
    for(int i = 0; i < numsSize; i++)
    {
        hashTable[nums[i] + offset]++;
    }

确定了大小,下一步就是生成这张hash表,然后用memset函数把hash表全部置为0,然后循环为每个输入的数找到对应的hash表的位置,然后将对应的hash表的值加1.

    int **result = (int**)malloc(sizeof(int*) * 300);
    *returnSize = 0;
    for(int i = 0; i < numsSize; i++)
    {
        int target = -1 * nums[i];
        int *newHashTable = (int*)malloc(sizeof(int) * (max_element - min_element + 1));
        memcpy(newHashTable, hashTable, sizeof(int) * (max_element - min_element + 1));
        if(newHashTable[nums[i] + offset] > 0)
            newHashTable[nums[i] + offset]--;
        else if(newHashTable[nums[i] + offset] == 0)
            continue;
        twoSum(nums, numsSize, returnSize, newHashTable, max_element - min_element + 1, target, i+1, offset, result, nums[i]);
        hashTable[nums[i] + offset] = 0;

    }

    return result;
}

首先开一个存储结果的二维数组,这个地方我设的值是300,也就是说最多支持300种三维组,当然如果册数数据很大,那么很可能结果会多于300种,但就这道题而言300够用了。然后我们进入到循环之中,按顺序取数组中每一个数,由于要求相加的结果为0,所以我们的目标target就是剩下的两个数的相加之和是第一个数的相反数,然我们复制当前的hash表,原因是在每次进入twoSum之前,hash表都会做特定的修改,而这个修改只在这一次迭代中起作用,下次迭代时我们可能并不需要这个修改,要使用原本的hash表。因此每次迭代我们都将原本的hash表复制一张,然后然后复制的hash表仅用于这一次的迭代。

产生了新的hash表之后,我们当前的这个值在hash表中的值,如果大于0,说明这个值存在于输入中,因此我们使用了当前值之后,当前值对应的数量就会减少1. 如果当前值对应的hash表中的值为0,说明这个值已经不能再使用了(有可能是当前值出现了多次,之前已经使用过当前值作第一个值了,所有可能的组合都找出来了),所以直接跳过进入下一次的迭代。

之后我们就可以进入twoSum函数,即找出两个值,它们之和为target,当twoSum函数返回后,把当前值置为0,因为所有使用当前值作为第一个值得情况我们已经都找出来了,如果之后又碰到当前值做第一个值,那我们就可以直接跳过了。

void twoSum(int* nums, int numsSize, int* returnSize, int *hashTable, int hTlen, int target, int start, int offset, int**result, int current)
{
    int NeedValue;

    int count = *returnSize;
    for(int i = start; i < numsSize; i++)
    {
        if(!hashTable[nums[i]+offset])
            continue;
        hashTable[nums[i]+offset]--;
        NeedValue = target - nums[i];
        if(NeedValue + offset < 0 || NeedValue + offset > hTlen - 1)
        {
            hashTable[nums[i]+offset] = 0;
            continue;
        }

Two sum这个函数是我们的核心,首先定义NeedValue, 它不同于target,它等于target减去第二个数,也就是说它是我们想要的第三个数的值。然后我们从第一个数的下一个位置开始做为第二个数,这样保证了,第二个数b始终在第一个数a的后面,这样就避免了和之前搜索结果重复的情况,同样第三个数c一定在第二个数b后面,否则,我们就会在搜索到b之前,先搜索到第三个数c,之后我们就会找到b作为第三个数,找到这个组合后,我们会把b,c对应的hash表都置为0,因此在遇到b时直接跳过,不会搜索。而我们现在在搜索b,就说明第三个数c一定在b后面,同时b一定在a后面。 在循环中,我们首先查看当前正在搜索的值对应的hash值,如果为0,那么这个值不可用,直接跳过,找下一个搜索值。否则对应的hash值减1,我们采用当前搜索值作为第二个数,所以剩下的具有这个值的可用的数少了一个。在确定第二个值之后我们,就可以确定我们需要的第三个值了,如果我们想要的第三个值对应的索引值不在hash表范围内,那么必然不存在,所以把第二个值对应的hash表的值置为0,因为第二个值选它不可能找到匹配,然后进入下一次迭代。

        if(hashTable[NeedValue+offset])
        {
            result[count] = (int*)malloc(sizeof(int) * 3);
            result[count][0] = nums[i] < NeedValue ? nums[i] : NeedValue;
            result[count][1] = nums[i] > NeedValue ? nums[i] : NeedValue;

            if(current >= result[count][1])
                result[count][2] = current;
            else if(current < result[count][1] && current >= result[count][0])
            {
                result[count][2] = result[count][1];
                result[count][1] = current;
            }
            else
            {
                result[count][2] = result[count][1];
                result[count][1] = result[count][0];
                result[count][0] = current;
            }

            count++;
        }

        hashTable[NeedValue+offset] = 0;
        hashTable[nums[i]+offset] = 0;
    },
    *returnSize = count;
}

如果需要的值刚好在,那么我们就找到了一个符合要求的三维组,然后我们按由小到大的顺序把它们排好,存到result中,然后把第二个数和第三个数对应的hash表的值置为0,因为如果之后再遇到第二个值,那么要想符合要求也只能找第三个值,而这我们已经找到过了,所以忽略掉,如果再遇到第三个值,那么它也只能找第二个值,所以同样忽略掉。另外如果需要的值不存在,同样把第二个值和第三个值对应的hash表的值置为0,其实第三个值的值已经是0,这就是说以后再遇到第二个值,可以忽略,因为它只能找第三个值,而第三个值不可取,所以不能成功。

下面是完整程序代码:

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

void twoSum(int* nums, int numsSize, int* returnSize, int *hashTable, int hTlen, int target, int start, int offset, int**result, int current)
{
    int NeedValue;

    int count = *returnSize;
    for(int i = start; i < numsSize; i++)
    {
        if(!hashTable[nums[i]+offset])
            continue;
        hashTable[nums[i]+offset]--;
        NeedValue = target - nums[i];
        if(NeedValue + offset < 0 || NeedValue + offset > hTlen - 1)
        {
            hashTable[nums[i]+offset] = 0;
            continue;
        }
        if(hashTable[NeedValue+offset])
        {
            result[count] = (int*)malloc(sizeof(int) * 3);
            result[count][0] = nums[i] < NeedValue ? nums[i] : NeedValue;
            result[count][1] = nums[i] > NeedValue ? nums[i] : NeedValue;

            if(current >= result[count][1])
                result[count][2] = current;
            else if(current < result[count][1] && current >= result[count][0])
            {
                result[count][2] = result[count][1];
                result[count][1] = current;
            }
            else
            {
                result[count][2] = result[count][1];
                result[count][1] = result[count][0];
                result[count][0] = current;
            }

            count++;
        }

        hashTable[NeedValue+offset] = 0;
        hashTable[nums[i]+offset] = 0;
    }
    *returnSize = count;
}

int** threeSum(int* nums, int numsSize, int* returnSize)
{
    if(numsSize == 0)
        return NULL;
    int min_element = 100000000;
    int max_element = -100000000;
    for(int i = 0; i < numsSize; i++)
    {
        if(min_element > nums[i])
            min_element = nums[i];
        if(max_element < nums[i])
            max_element = nums[i];
    }

    int offset = -1* min_element;

    int *hashTable = (int*)malloc(sizeof(int) * (max_element - min_element + 1));

    memset(hashTable, 0, sizeof(int) * (max_element - min_element + 1));
    for(int i = 0; i < numsSize; i++)
    {
        hashTable[nums[i] + offset]++;
    }

    int **result = (int**)malloc(sizeof(int*) * 300);
    *returnSize = 0;
    for(int i = 0; i < numsSize; i++)
    {
        int target = -1 * nums[i];
        int *newHashTable = (int*)malloc(sizeof(int) * (max_element - min_element + 1));
        memcpy(newHashTable, hashTable, sizeof(int) * (max_element - min_element + 1));
        if(newHashTable[nums[i] + offset] > 0)
            newHashTable[nums[i] + offset]--;
        else if(newHashTable[nums[i] + offset] == 0)
            continue;
        //else if(newHashTable[nums[i] + offset] < 0)
            //cout<<"asdfadsfasdfasdfasdfasdf";
        twoSum(nums, numsSize, returnSize, newHashTable, max_element - min_element + 1, target, i+1, offset, result, nums[i]);
        hashTable[nums[i] + offset] = 0;

    }

    return result;
}

int main()
{
    int nums[300], numsSize = 0, returnSize = 0;

    cout<<"Input the numSize:"<<endl;
    cin>>numsSize;

    for(int i = 0; i < numsSize; i++)
    {
        cin>>nums[i];
    }


    int **result = threeSum(nums, numsSize, &returnSize);

    for(int i = 0; i < returnSize; i++)
    {
        cout<<result[i][0]<<" "<<result[i][1]<<" "<<result[i][2]<<endl;;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值