mzoe666888

V1

2022/04/08阅读:21主题:兰青

内卷大厂系列《元组问题六连击》

大厂高频算法面试题:《元组问题系列》,您将学到如何使用双指针技巧解决单调性问题。

欢迎关注《内卷学苑》

一、元组问题 I

给定一个有序数组arr,给定一个正数aim,打印累加和为aim的所有不同二元组

1、分析

二元组问题:利用双指针,题目是有序数组,有三种可能性:

  • 可能性一:arr[L] + arr[R] < aim,L++
  • 可能性二:arr[L] + arr[R] = aim,三种情况都可以
    • L++,收集答案,但需要有排重机制,L-1 ≠ L作为排重条件
    • R--,R ≠ R+1 作为排重条件
    • L++,R--,L-1 ≠ L 或 R ≠ R+1 作为排重条件,这种机制是有单调性的
  • 可能性三:arr[L] + arr[R] > aim,R--

有序数组具有单调性,L++,R--,如果不是有序数组,需要对数组进行排序,才可以利用双指针技巧

2、实现

public static void printUniquePair(int[] arr, int aim) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int L = 0;
    int R = arr.length - 1;
    while (L < R) {
        if (arr[L] + arr[R] < aim) {
            L++;
        } else if (arr[L] + arr[R] > aim) {
            R--;
        } else { // L + R = aim
            if (L == 0 || arr[L - 1] != arr[L]) {
                System.out.println(arr[L] + "," + arr[R]);
            }
            L++;
            R--;
        }
    }
}

二、元组问题 II

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值target的那两个整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

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

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

两数之和:leetcode

1、分析

二元组问题

方法一:数组先排序,利用双指针技巧

方法二:采用哈希表HashMap,key存数组中每个数,value存数对应的下标

2、实现

public static int[] twoSum(int[] nums, int target) {
    // key 某个之前的数   value 这个数出现的位置
    HashMap<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        if (map.containsKey(target - nums[i])) {
            return new int[]{map.get(target - nums[i]), i};
        }
        map.put(nums[i], i);
    }
    return new int[]{-1, -1};
}

三、元组问题 III

给定一个有序数组arr,给定一个正数aim,打印累加和为aim的所有不同三元组

1、分析

三元组问题:转化为二元组问题,当前来到i位置,求i+1~N-1范围上的两数累计和为aim - arr[i],也需要有排重机制

三元组问题转化为有边界的二元组问题

2、实现

public static void printUniqueTriad(int[] arr, int aim) {
    if (arr == null || arr.length < 3) {
        return;
    }
    for (int i = 0; i < arr.length - 2; i++) { // 0~N-3
        if (i == 0 || arr[i] != arr[i - 1]) {
            printRest(arr, i, i + 1, arr.length - 1, aim - arr[i]);
        }
    }
}

public static void printRest(int[] arr, int f, int L, int R, int aim) {
    while (L < R) {
        if (arr[L] + arr[R] < aim) {
            L++;
        } else if (arr[L] + arr[R] > aim) {
            R--;
        } else {
            if (L == f + 1 || arr[L - 1] != arr[L]) {
                System.out.println(arr[f] + "," + arr[L] + "," + arr[R]);
            }
            L++;
            R--;
        }
    }
}

四、元组问题 IV

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为0且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

三数之和:leetcode

1、分析

三元组问题可以转化为二元组问题,卡主第一个数(数组中的每一个数作为三元组的第一个数),然后就是二元组问题,如果之前出现过,则跳过,防重

把打印行为变为收集,收集的过程中需要考虑ArrayList插入方向效率问题

2、实现

public static List<List<Integer>> threeSum(int[] nums) {
    Arrays.sort(nums);
    int N = nums.length;
    List<List<Integer>> ans = new ArrayList<>();
    for (int i = N - 1; i > 1; i--) { // 从后往前遍历
        if (i == N - 1 || nums[i] != nums[i + 1]) {
            List<List<Integer>> nexts = twoSum(nums, i - 1, -nums[i]);
            for (List<Integer> cur : nexts) {
                cur.add(nums[i]); // 效率高,直接添加到结尾
                ans.add(cur);
            }
        }
    }
    return ans;
}

public static List<List<Integer>> twoSum(int[] nums, int end, int target) {
    int L = 0;
    int R = end;
    List<List<Integer>> ans = new ArrayList<>();
    while (L < R) {
        if (nums[L] + nums[R] > target) {
            R--;
        } else if (nums[L] + nums[R] < target) {
            L++;
        } else {
            if (L == 0 || nums[L - 1] != nums[L]) {
                List<Integer> cur = new ArrayList<>();
                cur.add(nums[L]);
                cur.add(nums[R]);
                ans.add(cur);
            }
            L++;
        }
    }
    return ans;
}

五、元组问题 V

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n

请你计算有多少个元组 (i, j, k, l) 能满足:

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

示例 1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例 2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1

四数之和 II:leetcode

1、分析

方法一:4个for循环,时间复杂度O(N^4)

方法二:2个for循环,时间复杂度O(N²),先两个for循环找出累加和及出现的次数,用map存起来,key:数组1和数组2中的每个数的累加和,value为累加和出现的次数,然后在拿两个for循环,根据数组3和数组4中的每个数的累加和,去从Map中找key为相反数的,value即为出现的元组

2、实现

public static int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
    HashMap<Integer, Integer> map = new HashMap<>();
    int sum = 0;
    for (int i = 0; i < A.length; i++) {
        for (int j = 0; j < B.length; j++) {
            sum = A[i] + B[j];
            if (!map.containsKey(sum)) {
                map.put(sum, 1);
            } else {
                map.put(sum, map.get(sum) + 1);
            }
        }
    }
    int ans = 0;
    for (int i = 0; i < C.length; i++) {
        for (int j = 0; j < D.length; j++) {
            sum = C[i] + D[j];
            if (map.containsKey(-sum)) {
                ans += map.get(-sum);
            }
        }
    }
    return ans;
}

六、元组问题 VI

给你一个由 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]]

四数之和 I:leetcode

1、分析

四数之和问题转化为三数之和问题,三数之和问题转化为两数之和问题

先排序,利用双指针

2、实现

public static List<List<Integer>> fourSum(int[] nums, int target) {
    Arrays.sort(nums);
    int N = nums.length;
    List<List<Integer>> ans = new ArrayList<>();
    for (int i = N - 1; i > 2; i--) {
        if (i == N - 1 || nums[i] != nums[i + 1]) {
            List<List<Integer>> nexts = threeSum(nums, i - 1, target - nums[i]);
            for (List<Integer> cur : nexts) {
                cur.add(nums[i]); // 效率高,直接添加到结尾
                ans.add(cur);
            }
        }
    }
    return ans;
}

public static List<List<Integer>> threeSum(int[] nums, int end, int target) {
    List<List<Integer>> ans = new ArrayList<>();
    for (int i = end; i > 1; i--) { // 从后往前遍历
        if (i == end || nums[i] != nums[i + 1]) {
            List<List<Integer>> nexts = twoSum(nums, i - 1, target - nums[i]);
            for (List<Integer> cur : nexts) {
                cur.add(nums[i]); // 效率高,直接添加到结尾
                ans.add(cur);
            }
        }
    }
    return ans;
}

public static List<List<Integer>> twoSum(int[] nums, int end, int target) {
    int L = 0;
    int R = end;
    List<List<Integer>> ans = new ArrayList<>();
    while (L < R) {
        if (nums[L] + nums[R] > target) {
            R--;
        } else if (nums[L] + nums[R] < target) {
            L++;
        } else {
            if (L == 0 || nums[L - 1] != nums[L]) {
                List<Integer> cur = new ArrayList<>();
                cur.add(nums[L]);
                cur.add(nums[R]);
                ans.add(cur);
            }
            L++;
        }
    }
    return ans;
}

分类:

后端

标签:

数据结构与算法

作者介绍

mzoe666888
V1