最小差

标签: 数组 双指针 二分查找 排序

难度: Medium

给定两个整数数组ab,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差

 

示例:

输入:{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8}
输出:3,即数值对(11, 8)

 

提示:

  • 1 <= a.length, b.length <= 100000
  • -2147483648 <= a[i], b[i] <= 2147483647
  • 正确结果在区间 [0, 2147483647]

Submission

运行时间: 96 ms

内存: 22.5 MB

class Solution:
    def smallestDifference(self, a: List[int], b: List[int]) -> int:
        a.sort()
        b.sort()
        n = len(a)
        m = len(b)
        ans = float('inf')
        if a[0] <= b[0]:
            flag = 0
        else:
            flag = 1
        i = 0
        j = 0
        while i < n and j < m:
            if flag == 0:
                while i < n and a[i] <= b[j]:
                    i += 1
                ans = min(ans, b[j] - a[i - 1])
                flag = 1
            else:
                while j < m and b[j] <= a[i]:
                    j += 1
                ans = min(ans, a[i] - b[j - 1])
                flag = 0
        return ans

Explain

此题解首先对两个数组进行排序,然后使用两个指针分别遍历这两个数组,比较当前指针所指的元素,以尽可能找到最小的差值。排序后的数组保证了元素是有序的,从而可以通过调整两个指针的位置来比较不同数组中的最接近的元素。指针移动的规则是:若当前a中的元素小于b中的元素,则移动指向a的指针;若b中的元素小于a中的元素,则移动指向b的指针。通过这种方式,能够高效地找到两个数组中差值最小的一对数。

时间复杂度: O(n log n + m log m)

空间复杂度: O(1)

class Solution:
    def smallestDifference(self, a: List[int], b: List[int]) -> int:
        a.sort()  # 对数组a进行排序
        b.sort()  # 对数组b进行排序
        n = len(a)  # 数组a的长度
        m = len(b)  # 数组b的长度
        ans = float('inf')  # 初始化最小差值为无限大
        if a[0] <= b[0]:
            flag = 0  # 初始设置flag,决定比较的方向
        else:
            flag = 1
        i = 0  # a的索引
        j = 0  # b的索引
        while i < n and j < m:  # 只要索引没有超出各自数组的长度
            if flag == 0:
                while i < n and a[i] <= b[j]:  # 在a中寻找比b[j]大的第一个元素
                    i += 1
                if i > 0:  # 确保i不越界
                    ans = min(ans, b[j] - a[i - 1])  # 更新最小差值
                flag = 1  # 更改flag,下一轮比较b[j]和a[i]
            else:
                while j < m and b[j] <= a[i]:  # 在b中寻找比a[i]大的第一个元素
                    j += 1
                if j > 0:  # 确保j不越界
                    ans = min(ans, a[i] - b[j - 1])  # 更新最小差值
                flag = 0  # 更改flag,下一轮比较a[i]和b[j]
        return ans  # 返回找到的最小差值

Explore

排序两个数组的目的是为了将数组内的元素按照升序排列,这样在使用双指针遍历时可以有效地比较和移动指针。在有序的数组中,任意两个相邻的元素之间的差值是逐渐增大或保持的。因此,通过比较两个有序数组中的元素,可以更系统地寻找并缩小两数组元素间的最小差值。当指针指向的两个元素相差较大时,可以通过移动较小元素所在数组的指针来尝试找到更小的差值,这种方法保证了不会错过任何可能的最小差值对。

这种方法不会跳过潜在的最小差值对。原因是在有序数组中,如果`a[i]`逐渐逼近`b[j]`,则每次`i`指针的移动都是为了寻找更接近`b[j]`的值。当`a[i]`超过`b[j]`时,已经尝试了所有可能使`a[i]`接近`b[j]`的组合。然而,当`a[i]`刚好小于`b[j]`而后一个元素又大于`b[j]`时,实际已经包括了与`b[j]`差值最小的可能性。因此,即使继续移动i指针,也不会有更小的差值存在。

使用`flag`变量的目的是为了在每次比较后控制下一次的比较方向,这样似乎可以避免重复的比较和不必要的指针移动。然而,这种方法增加了代码的复杂性和理解难度。更简洁的方法是直接在每次循环中比较`a[i]`和`b[j]`,并基于比较结果直接决定移动哪个指针。如果`a[i]`小于`b[j]`,则移动i;如果`b[j]`小于`a[i]`,则移动j。这样直接根据两元素的大小关系来移动指针,可以使逻辑更直接、代码更清晰。