1. 程式人生 > >Leetcode演算法Java全解答--18. 四數之和

Leetcode演算法Java全解答--18. 四數之和

Leetcode演算法Java全解答–18. 四數之和

題目

給定一個包含 n 個整數的陣列 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。

注意:

答案中不可以包含重複的四元組。

示例:

給定陣列 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

滿足要求的四元組集合為:
[
    [-1,  0, 0, 1],
    [-2, -1, 1, 2],
    [-2,  0, 0, 2]
]

想法

  1. 暴力破解:四層迴圈 一把梭
  2. 套用三層迴圈的思維

原來三數之和 迴圈一次加滑動列表 改成 迴圈兩次加滑動列表

複雜度:n3/n

結果

超過91%的測試案例

時間複雜度/空間複雜度:n3/n

總結

之前卡在這題沒有去做,就是自己沒有想著動手寫

程式碼

我的答案

   
/**************************************
 * 題目
 給定一個包含 n 個整數的陣列 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。

 注意:

 答案中不可以包含重複的四元組。

 示例:

 給定陣列 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

 滿足要求的四元組集合為:
 [
 [-1,  0, 0, 1],
 [-2, -1, 1, 2],
 [-2,  0, 0, 2]
 ]
 **************************************/

/**************************************
 *
 * 想法:
 *      1. 暴力破解:四層迴圈 一把梭
 *      2. 套用三層迴圈的思維
 *          原來 迴圈一次加滑動列表 改成 迴圈兩次加滑動列表
 *          詳細可以看這個:
 *          複雜度:n3/n
 *
 * 我的做法
 *      超過91%的測試案例
 *      時間複雜度/空間複雜度:n3/n
 * 程式碼執行過程:
 *
 * 總結:
 *      1. 之前卡在這題沒有去做,就是自己沒有想著動手寫
 * ***********************************/
public List<List<Integer>> fourSum(int[] nums, int target) {
    if (nums == null || nums.length < 4) {
        return Collections.EMPTY_LIST;
    }
    Arrays.sort(nums);

    List<List<Integer>> result = new ArrayList<>();
    for (int i = 0; i < nums.length - 3; i++) {

        // 如果和之前那個資料相同,則會是重複事件
        if (i > 0 && nums[i] == nums[i - 1]) {
            continue;
        }
        int valI = nums[i];

        for (int j = i + 1; j < nums.length - 2; j++) {

            // 如果和之前那個資料相同,則會是重複事件
            if (j - i > 1 && nums[j] == nums[j - 1]) {
                continue;
            }

            int valJ = nums[j];

            int leftIndex = j + 1;
            int rightIndex = nums.length - 1;

            while (leftIndex < rightIndex) {
                int tmpVal = nums[leftIndex] + nums[rightIndex] + valI + valJ;

                if (tmpVal > target) {
                    rightIndex--;
                } else if (tmpVal < target) {
                    leftIndex++;
                } else {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[j]);
                    list.add(nums[leftIndex]);
                    list.add(nums[rightIndex]);
                    result.add(list);
                    // 用nums[leftIndex] == nums[leftIndex + 1]控制重複
                    while (leftIndex < rightIndex && nums[leftIndex] == nums[leftIndex + 1]) {
                        leftIndex++;
                    }
                    while (leftIndex < rightIndex && nums[rightIndex] == nums[rightIndex - 1]) {
                        rightIndex--;
                    }
                    leftIndex++;
                    rightIndex--;
                }
            }
        }
    }
    return result;
}

大佬們的答案

/**************************************
 * 比我好的答案 better
 * ***********************************/
public List<List<Integer>> better(int[] nums, int target) {
    List<List<Integer>> result = new ArrayList<>();
    int len = nums.length;
    if (nums == null || len < 4) {
        return result;
    }
    //排序
    Arrays.sort(nums);
    //確定兩個值兩層迴圈然後確定左右指標
    //外迴圈
    for (int i = 0; i < len - 3; i++) {
        //判斷相等
        if (i != 0 && nums[i] == nums[i - 1]) {
            continue;
        }
        //最小值大於目標值結束
        if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
            break;
        }
        //最大值小於目標值跳過此迴圈
        if (nums[i] + nums[len - 1] + nums[len - 2] + nums[len - 3] < target) {
            continue;
        }
        //內迴圈
        for (int j = i + 1; j < len - 2; j++) {
            //判斷相等
            if (j > i + 1 && nums[j] == nums[j - 1]) {
                continue;
            }
            //最小值大於目標值結束
            if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                break;
            }
            //最大值小於目標值跳過此迴圈
            if (nums[i] + nums[j] + nums[len - 2] + nums[len - 1] < target) {
                continue;
            }
            //左右指標尋找滿足條件的值
            int left = j + 1;
            int right = len - 1;
            while (left < right) {
                int sum = nums[left] + nums[right] + nums[i] + nums[j];
                if (sum == target) {
                    result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }
                } else if (sum < target) {
                    left++;
                } else {
                    right--;
                }

            }
        }
    }
    return result;
}

測試用例

@Test
public void test018() {
    // 建立測試案例
    //        int[] arr1 = new int[] { 1, 0, -1, 0, -2, 2 };
    int[] arr1 = new int[] { 0, 0, 0, 0 };
    int target1 = 0;

    // 測試案例期望值
    List<List<Integer>> expResult1 = new ArrayList<>();

    List<Integer> list1 = new ArrayList<>();
    list1.add(-1);
    list1.add(0);
    list1.add(0);
    list1.add(1);
    List<Integer> list2 = new ArrayList<>();
    list2.add(-2);
    list2.add(-1);
    list2.add(1);
    list2.add(2);
    List<Integer> list3 = new ArrayList<>();
    list2.add(-2);
    list2.add(0);
    list2.add(0);
    list2.add(2);
    expResult1.add(list1);
    expResult1.add(list2);
    expResult1.add(list3);

    // 執行方法
    Solution018 solution018 = new Solution018();
    List<List<Integer>> result1 = solution018.fourSum(arr1, target1);

    // 判斷期望值與實際值
    Assert.assertEquals(expResult1, result1);

}

其他

“大佬們的答案” 標籤來自leetcode,侵權請聯絡我進行刪改

如有疑問請聯絡,聯絡方式:QQ3060507060