leetcode 454. 四数相加 II c语言

如题:

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终
结果不会超过 231 - 1 。

例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

这道题在hash专项练习里遇到,最初的方法是循环法(俗称暴力),遍历4个数组,计算每种组合,时间复杂度在O(n^4)。复杂度还蛮大的,应该可以优化,想了半天,没想到啥巧妙的方法。看了其它人的题解,可以优化到O(n^2),思路:遍历AB数组,计算每种组合的和,将和放入到hash桶中,key为和,值为次数。时间复杂度为O(n^2)。接着遍历CD数组,计算各种组合s,查找hash中是否存在-s,找到则累积可能性。时间复杂度为O(n^2)。总的时间复杂度为O(2n^2)。和O(n^4)相比,的确优化了不少。hash需要占空间,空间换时间,不亏。思路倒是很简单,但是的确是没想到,其实就是减少内层循环的次数。遇到同样的题型都可以如此优化。有学会一招。下面是是c语言的解法:

/*
 *  解法1:暴力遍历,O(n^4)
 *  解法2:使用hash优化暴力算法,O(n^2)
 */

typedef struct hlist {
    int nkey, pkey;
    int nNum, pNum;
    struct hlist *next;
}*hnode;

hnode newHashNode(int key)
{
    hnode n = (hnode)calloc(1, sizeof(struct hlist));
    if (key < 0){
        n->nkey = key;
        n->pkey = 0 - key;
        n->nNum = 1;
    }else{
        n->pkey = key;
        n->nkey = 0 - key;
        n->pNum = 1;      
        if (key == 0)
            n->nNum = 1;
    }
    return n;    
}

int hashKey(int s)
{
    if (s < 0)
        return (0 - s) % 10000;
    else
        return s % 10000;
}

int fourSumCount(int* A, int ASize, int* B, int BSize, int* C, int CSize, int* D, int DSize){
    int i,j,n = 0,s, key;
    hnode hash[10000] = {0}, prev, curr, new;
    
    //特殊情况处理
    if (ASize == 0 || BSize == 0 ||CSize == 0 || DSize == 0)
        return 0;
    
    //遍历AB和,将其加入到hash桶中,和为key,值为次数
    for (i = 0; i < ASize; i++)
    {
        for (j = 0; j < BSize; j++)
        {
            s = A[i] + B[j];
            key = hashKey(s);
            prev = NULL;
            curr = hash[key];
            while (curr)
            {
                if (curr->nkey == s)
                {
                    curr->nNum++;
                    break;
                }
                else if (curr->pkey == s)
                {
                    curr->pNum++;
                    break;
                }
                else
                {
                    prev = curr;
                    curr = curr->next;
                }
            }
            if (!curr)
            {
                new = newHashNode(s);
                if (prev)
                    prev->next = new;
                else
                    hash[key] = new;
            }                        
        }
    }
    
    //遍历CD和s,在hash表中查找-s,统计次数。
    for (i = 0; i < CSize; i++)
    {
        for (j = 0; j < DSize; j++)
        {
            s = C[i] + D[j];
            s = 0 - s;
            key = hashKey(s);
            
            curr = hash[key];
            while (curr)
            {
                if (curr->nkey == s){
                    n += curr->nNum;
                    break;
                }                    
                else if (curr->pkey == s){
                    n += curr->pNum;
                    break;
                }                    
                else
                    curr = curr->next;
            }
            
        }
    }
    return n;
}

 

=============================================================================================

Linux应用程序、内核、驱动开发交流讨论群(745510310),对后端、互联网感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。
--------------------- 
版权声明:本文为CSDN博主「mrsonko」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fuyuande/article/details/99434278

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值