題目:
給你一個由 n 個整數組成的數組 nums ,和一個目標值 target 。請你找出并返回滿足下述全部條件且不重復的四元組 [nums[a], nums[b], nums[c], nums[d]] (若兩個四元組元素一一對應,則認為兩個四元組重復):
- 0 <= a, b, c, d < n
- a、b、c 和 d 互不相同
- nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意順序 返回答案 。
示例 1:
輸入:nums = [1,0,-1,0,-2,2], target = 0
輸出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
輸入:nums = [2,2,2,2,2], target = 8
輸出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
代碼:
#include <stdlib.h>// 比較函數,用于 qsort 對數組進行排序
// pa 和 pb 是指向要比較元素的指針
// 返回值為 1 表示 a > b,返回 -1 表示 a < b
int cmp(const void* pa, const void* pb)
{int a = *(int*)pa; // 將 pa 指針指向的元素轉換為 int 類型并賦值給 aint b = *(int*)pb; // 將 pb 指針指向的元素轉換為 int 類型并賦值給 breturn a > b ? 1 : -1; // 如果 a 大于 b 返回 1,否則返回 -1
}// 四數之和函數,用于找出數組中所有不重復的四元組,使得它們的和等于目標值
// nums 是輸入的整數數組
// numsSize 是數組的長度
// target 是目標和
// returnSize 是返回的四元組的數量
// returnColumnSizes 是每個四元組的長度(固定為 4)
int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes) {qsort(nums, numsSize, sizeof(int), cmp); // 對數組進行排序,方便后續去重和雙指針操作int base = 100; // 初始分配的結果數組的容量int **result = (int**)malloc(sizeof(int*) * base); // 動態分配存儲四元組的二維數組*returnColumnSizes = (int*)malloc(sizeof(int) * base); // 動態分配存儲每個四元組長度的數組*returnSize = 0; // 初始化返回的四元組數量為 0// 如果數組長度小于 4,直接返回空結果if (numsSize < 4) {return result;}// 外層循環,固定第一個數for(int i = 0; i < numsSize - 3; i++) {// 跳過重復的第一個數,避免結果中出現重復的四元組if (i > 0 && nums[i] == nums[i - 1])continue;// 第二層循環,固定第二個數for(int j = i + 1; j < numsSize - 2; j++) {// 跳過重復的第二個數,避免結果中出現重復的四元組if (j > i + 1 && nums[j] == nums[j - 1])continue;int l = j + 1; // 左指針,指向第二個數的下一個位置int r = numsSize - 1; // 右指針,指向數組的最后一個位置// 雙指針法,在剩余的元素中尋找滿足條件的第三個數和第四個數while (l < r) {// 計算四個數的和,使用 long long 類型避免整數溢出long long sum = (long long)nums[i] + (long long)nums[j] + (long long)nums[l] + (long long)nums[r];long long temp = sum - target; // 計算當前和與目標值的差值// 如果差值為 0,說明找到了一個滿足條件的四元組if (temp == 0) {result[*returnSize] = (int*)malloc(sizeof(int) * 4); // 為新的四元組分配內存(*returnColumnSizes)[*returnSize] = 4; // 設置該四元組的長度為 4result[*returnSize][0] = nums[i]; // 存儲第一個數result[*returnSize][1] = nums[j]; // 存儲第二個數result[*returnSize][2] = nums[l]; // 存儲第三個數result[*returnSize][3] = nums[r]; // 存儲第四個數(*returnSize)++; // 四元組數量加 1// 如果結果數組的容量不夠,擴大容量if (*returnSize == base) {base *= 2; // 容量翻倍result = (int**)realloc(result, sizeof(int*) * base); // 重新分配結果數組的內存*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * base); // 重新分配每個四元組長度數組的內存}int a = nums[l]; // 記錄當前左指針指向的數int b = nums[r]; // 記錄當前右指針指向的數// 跳過重復的第三個數while (nums[l] == a && l < r)l++;// 跳過重復的第四個數while (nums[r] == b && l < r)r--;} // 如果當前和大于目標值,右指針左移else if (temp > 0) {r--;} // 如果當前和小于目標值,左指針右移else {l++;} }}}return result; // 返回存儲所有滿足條件四元組的二維數組
}
代碼分析:
優點
- 排序和雙指針法:通過對數組進行排序,然后使用雙指針法,將原本的暴力枚舉 O(n4)的時間復雜度降低到了O(n3),提高了算法的效率。
- 去重處理:在每一層循環中都進行了去重處理,避免了結果中出現重復的四元組,保證了結果的正確性。
- 動態內存分配:使用 malloc 和 realloc 動態分配內存,根據實際結果的數量調整內存大小,避免了內存的浪費。
- 處理整數溢出:在計算四個數的和時,使用 long long 類型,避免了整數溢出的問題,提高了代碼的健壯性。
缺點
- 時間復雜度較高:雖然使用了雙指針法將時間復雜度從 O(n4)的時間復雜度降低到了O(n3),但對于大規模數據,仍然可能會超時。
- 內存管理復雜:使用了動態內存分配,需要手動管理內存,容易出現內存泄漏的問題。在使用完返回的結果數組后,需要調用者手動釋放內存。
- 提前剪枝不足:代碼中雖然有一些基本的邏輯,但沒有充分利用排序后的數組特性進行更有效的提前剪枝,例如可以在某些情況下提前終止循環,減少不必要的計算。