代码随想录算法训练营第七天| 454.四数相加II 、 383. 赎金信 、 15. 三数之和 、 18. 四数之和

文章讲述了如何通过构建哈希表和双指针技巧解决四个整数数组之和等于零的问题,涉及到了数组操作、哈希查找以及去重算法的应用。
摘要由CSDN通过智能技术生成

454.四数相加II

题目链接/文章讲解/视频讲解:代码随想录

给你四个整数数组 nums1nums2nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

分析及思路

我们是四个数组相加最终等于0,我们可以两个两个的进行相加,a[]+b[]然后c[]+d[],即a[]+b[]等于

-(c[]+d[]),就转换成,查哈希表格了,因为要存储下标和出现的次数,所以我们用map来解决问题。

详细请看:代码随想录 (programmercarl.com)

 代码及注释

typedef struct { // 定义一个结构体
     int key; // 键
     int value; // 值
     UT_hash_handle hh; // 使这个结构体可哈希化
 } map; // 结构体类型为map

int fourSumCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* nums3, int nums3Size, int* nums4, int nums4Size){
    map* hashMap = NULL;  // 声明一个指向 map 结构体的指针 hashMap,初始化为 NULL
    map* temp = NULL;  // 声明一个指向 map 结构体的指针 temp,初始化为 NULL
    int key;  // 声明一个整型变量 key
    int count = 0;  // 声明一个整型变量 count,初始化为 0
    for(int i=0;i<nums1Size;i++){  // 循环,从 i=0 开始,直到 i<nums1Size 为止,每次递增 i
        for(int j=0;j<nums2Size;j++){  // 嵌套循环,从 j=0 开始,直到 j<nums2Size 为止,每次递增 j
            key = nums1[i]+nums2[j];  // 计算 key 的值为 nums1[i] 加上 nums2[j]
            HASH_FIND_INT(hashMap,&key,temp);  // 在 hashMap 中查找 key 对应的元素,并将结果存储在 temp 中
            if(temp){  // 如果 temp 不为空
                temp->value++;  // temp 指向的元素的 value 值加一
            }
            else{  // 如果 temp 为空
                map* temp1 = (map*)malloc(sizeof(map));  // 分配内存空间,大小为 map 结构体的大小,并将地址赋给 temp1
                temp1->value = 1;  // 设置 temp1 指向的元素的 value 值为 1
                temp1->key = key;  // 设置 temp1 指向的元素的 key 值为 key
                HASH_ADD_INT(hashMap,key,temp1);  // 将 temp1 插入到 hashMap 中,key 为 key
            }
        }
    }
    for(int i=0;i<nums3Size;i++){  // 循环,从 i=0 开始,直到 i<nums3Size 为止,每次递增 i
        for(int j=0;j<nums4Size;j++){  // 嵌套循环,从 j=0 开始,直到 j<nums4Size 为止,每次递增 j
            key = 0-(nums3[i] +nums4[j]);  // 计算 key 的值为 0 减去 nums3[i] 加上 nums4[j]
            HASH_FIND_INT(hashMap,&key,temp);  // 在 hashMap 中查找 key 对应的元素,并将结果存储在 temp 中
            if(temp){  // 如果 temp 不为空
                count = count + temp->value;  // 将 count 值加上 temp 指向的元素的 value 值
            }
        }
    }
    return count;  // 返回 count 的值
}

383. 赎金信 

题目链接/文章讲解:代码随想录

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

分析及思路

和字母异位词一样使用数组哈希表,我们用大小为26的数组存储所有的字母,将第二个字母的频率存入数组中,然后用第一个字母进行频率的减法操作,若有一个小于0则返回false否则返回true,在此之前我们可以看一看第一个数组是否大于第二个数组,若大于则一定不存在,这样可以增加我们的代码效率。

代码及注释

bool canConstruct(char* ransomNote, char* magazine) { // 定义函数,参数为赎金信和杂志字符串
    int hash[26] = {0}; // 创建一个包含26个元素的哈希表,初始化为0
    if(strlen(ransomNote) > strlen(magazine)) // 如果赎金信的长度大于杂志的长度,返回false
        return false;
    for(int i = 0;i < strlen(magazine);i++){ // 遍历杂志字符串
        hash[magazine[i]-'a']++; // 将杂志字符串中每个字符出现的次数记录到哈希表中
    }
    for(int i = 0;i < strlen(ransomNote);i++){ // 遍历赎金信字符串
        hash[ransomNote[i]-'a']--; // 将赎金信字符串中每个字符出现的次数从哈希表中减去
    }
    for(int i= 0;i < 26;i++){ // 遍历哈希表
        if(hash[i] < 0) // 如果哈希表中有任何元素小于0,说明赎金信中出现了杂志中没有的字符,返回false
            return false;
    }
    return true; // 如果以上条件都不满足,返回true
}

15. 三数之和

题目链接/文章讲解/视频讲解:代码随想录

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

双指针

分析及思路

 这是一个在数组中求和的问题,可以使用三个指针,指向不同的元素,为了防止重复我们可以进行排序然后在求和,方便去重。类似滑动窗口。

代码及注释 

int cmp(const void* ptr1, const void* ptr2) { // 定义一个比较函数,用于qsort排序
    return *((int*)ptr1) > *((int*)ptr2); // 比较两个整数的大小
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) { // 定义一个函数,用于求解三数之和
    int** arr = (int**)malloc(sizeof(int*)*18000); // 为存储结果开辟空间
    int arr_Top = 0; // 用于记录结果数组的当前索引
    if(numsSize < 3){ // 如果数组长度小于3
        *returnSize = 0; // 返回的数组大小为0
        return arr; // 返回空数组
    }
    qsort(nums, numsSize, sizeof(int), cmp); // 对输入数组进行排序
    for(int i = 0;i<numsSize-2;i++){ // 遍历数组
        if(nums[i] > 0) // 如果当前元素大于0,跳出循环
            break;
        if(i>0&&nums[i]==nums[i-1]) // 如果当前元素与前一个元素相等,跳过当前循环
            continue;
        int left = i + 1,right = numsSize-1; // 初始化左右指针
        while(left < right){ // 当左指针小于右指针时循环
            int sum = nums[i] + nums[left] + nums[right]; // 计算三数之和
            if(sum < 0) // 如果和小于0,左指针右移
                left++;
            else if(sum > 0) // 如果和大于0,右指针左移
                right--;
            else{ // 如果和等于0
                int* a = (int*)malloc( sizeof(int) * 3 ); // 为当前解分配空间
                a[0] = nums[i]; // 记录第一个数
                a[1] = nums[left]; // 记录第二个数
                a[2] = nums[right]; // 记录第三个数
                arr[arr_Top++] = a; // 将当前解存入结果数组
                while( (left<right) && (nums[left]==nums[left+1]) ) // 跳过重复的左指针
                    left++;
                while( (left<right) && nums[right]==nums[right-1] ) // 跳过重复的右指针
                    right--;
                left++; // 左指针右移
                right--; // 右指针左移
            }
        }
    }
    //设定返回的数组大小
    *returnSize = arr_Top; // 设置返回数组的大小
    *returnColumnSizes = (int*)malloc(sizeof(int) * arr_Top); // 为返回的列大小数组分配空间
    int z;
    for(z = 0; z < arr_Top; z++) { // 遍历结果数组
        (*returnColumnSizes)[z] = 3; // 设置每个解的列大小为3
    }
    return arr; // 返回结果数组
}

 18. 四数之和 

题目链接/文章讲解/视频讲解:代码随想录

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

分析及思路 

 和三数之和类似,我们还是使用,我们已久使用双指针,只是多增加了一个for循环。

代码及注释 

int cmp(const void* ptr1, const void* ptr2) { // 定义一个比较函数,用于qsort排序
    return *((int*)ptr1) > *((int*)ptr2); // 比较两个整数的大小
}
定义一个比较函数cmp,用于在qsort排序时进行比较。

int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes) {
    int arr_Top = 0;
    int** arr = (int**)malloc(sizeof(int*)*1001);
    用于存储结果的二维数组arr,初始分配1001个指针大小的空间。

    if(numsSize < 4){
        *returnSize = 0;
        return arr;
    }    
    如果输入数组大小小于4,则直接返回空数组。

    qsort(nums, numsSize, sizeof(int), cmp); // 对输入数组进行排序
    使用定义的比较函数cmp对输入数组进行排序。

    for(int k=0;k<numsSize-3;k++){
        if(nums[k]>target&&target>0){
            break;
        }
        if( (k>0) && (nums[k]==nums[k-1]) )
            continue;
        对于每个下标k,进行去重和边界判断。

        for(int i=k+1;i<numsSize-2;i++){
            if( (nums[i]>target) && (target>0) )
                break;
            if( (i>k+1) && (nums[i]==nums[i-1]) )
                continue;
            对于每个下标i,进行去重和边界判断。

            int left = i+1,right = numsSize - 1;
            定义左右指针left和right。

            while(left < right){
                long sum = (long)nums[k] + nums[i] + nums[left] + nums[right];
                计算当前四个数的和sum。

                if(sum > target)
                    right--;
                else if(sum < target){
                    left++;
                }
                else{
                    int* a = (int*)malloc(sizeof(int)*4);
                    a[0] = nums[k];
                    a[1] = nums[i];
                    a[2] = nums[left];
                    a[3] = nums[right];
                    将符合条件的四个数存入临时数组a中,并加入结果数组arr。

                    arr[arr_Top++] = a;
                    更新结果数组的长度。

                    while( (left < right) && (nums[left]==nums[left+1]) )
                        left++;
                    while( (left < right) && (nums[right]==nums[right-1]) )
                        right--;
                    进行去重处理。

                    left++;
                    right--;
                    移动左右指针。
                }
            }
        }
    }    
    遍历所有可能的组合,找到符合条件的四个数的组合。

    *returnSize = arr_Top;
    更新返回结果的大小。

    *returnColumnSizes = (int*)malloc(sizeof(int)*arr_Top);
    为返回的列大小数组分配内存。

    for(int i=0;i<arr_Top;i++)
        (*returnColumnSizes)[i] = 4;
    设置返回的列大小数组的值为4。

    return arr;
    返回结果数组arr。
}

 总结

今天的题目还得在看一看,要灵活运用双指针操作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值