170. 两数之和 III - 数据结构设计


ID: 170
TITLE: 两数之和 III - 数据结构设计
TAG: Java,Python


方法一:

这是第一题两数之和的后续问题之一,两数之和要求一个返回列表中两个数字的索引,这两个数字的和等于目标值。

我们可以从两数之和中汲取灵感,把所有的输入数字保存在一个列表中。

两数之和的一个解决方法是双指针法,指针从两个方向迭代,互相逼近。

170. 两数之和 III - 数据结构设计_第1张图片

但是,双指针法的前提条件之一是输入列表有序。

现在有几个问题:

  • 函数 add(number) 在列表中插入新数字时,是否应该保证列表有序。
  • 或者在调回 find(value) 时按需进行排序?

我们将在后面的算法部分讨论上述两个问题。

算法:
先给出双指针在有序列表中找到两数之和的算法。

  • 初始化两个指针 lowhigh 分别指向列表的头尾。
  • 从两个方向同时进行迭代,要么找到两数之和的解,要么两个指针相遇。
  • 在每个步骤中,我们将根据不同的条件移动指针:
    • 如果当前指针指向元素的和小于目标值,则应该增加总和来满足目标值,即将 low 指针向前移动获得更大的值。
    • 如果当前指针指向元素的和大于目标值,则应该减少总和来满足目标值,即将 highlow 靠近来减少总和。
    • 如果当前指针指向元素的和等于目标值,则直接返回结果。
    • 如果两个指针相交,说明当前列表不存在组合成目标值的两个数。
class TwoSum(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.nums = []
        self.is_sorted = False


    def add(self, number):
        """
        Add the number to an internal data structure..
        :type number: int
        :rtype: None
        """
        # Inserting while maintaining the ascending order.
        # for index, num in enumerate(self.nums):
        #     if number <= num:
        #         self.nums.insert(index, number)
        #         return
        ## larger than any number
        #self.nums.append(number)

        self.nums.append(number)
        self.is_sorted = False
    

    def find(self, value):
        """
        Find if there exists any pair of numbers which sum is equal to the value.
        :type value: int
        :rtype: bool
        """
        if not self.is_sorted:
            self.nums.sort()

        low, high = 0, len(self.nums)-1
        while low < high:
            currSum = self.nums[low] + self.nums[high]
            if currSum < value:
                low += 1
            elif currSum > value:
                high -= 1
            else: # currSum == value
                return True
        
        return False
import java.util.Collections;

class TwoSum {
  private ArrayList<Integer> nums;
  private boolean is_sorted;

  /** Initialize your data structure here. */
  public TwoSum() {
    this.nums = new ArrayList<Integer>();
    this.is_sorted = false;
  }

  /** Add the number to an internal data structure.. */
  public void add(int number) {
    this.nums.add(number);
    this.is_sorted = false;
  }

  /** Find if there exists any pair of numbers which sum is equal to the value. */
  public boolean find(int value) {
    if (!this.is_sorted) {
      Collections.sort(this.nums);
    }
    int low = 0, high = this.nums.size() - 1;
    while (low < high) {
      int twosum = this.nums.get(low) + this.nums.get(high);
      if (twosum < value)
        low += 1;
      else if (twosum > value)
        high -= 1;
      else
        return true;
    }
    return false;
  }
}

我们会发现,add(number) 函数将被频繁调用,而 find(value) 将不被那么频繁调用。

这样的使用模式下,意味着我们应该减少 add(number) 函数的时间消耗,因而我们是在 find(value) 对列表进行排序,而不是在 add(number)

在哪个函数进行排序,都是可行的,只是对应该使用模式下在 add(number) 下进行排序就不是最佳的方案了。

并且,我们在 find(value) 中是按需排序,也就是当列表更新时,才进行排序。

复杂度分析

  • 时间复杂度:
    • add(number) O ( 1 ) \mathcal{O}(1) O(1)
    • find(value) O ( N ⋅ log ⁡ ( N ) ) \mathcal{O}(N \cdot \log(N)) O(Nlog(N)),在最坏的情况下,我们需要对列表进行排序和遍历整个列表,这需要 O ( N ⋅ log ⁡ ( N ) ) \mathcal{O}(N \cdot \log(N)) O(Nlog(N)) O ( N ) \mathcal{O}(N) O(N) 的时间。因此总的时间复杂度为 O ( N ⋅ log ⁡ ( N ) ) \mathcal{O}(N \cdot \log(N)) O(Nlog(N))
  • 空间复杂度: O ( N ) \mathcal{O}(N) O(N),其中 N N N 指的是列表中的元素个数。

方法二:哈希表

两数之和的另一个解决方案是用哈希表存储值到索引的映射关系。

给定一个目标值 S,对于每个数字 a,我们只需要验证哈希表中是否存在 S - a

众所周知,哈希表可以提供快速的查找和插入操作,非常适合上述要求。

算法:

  • 首先初始化一个哈希表。
  • add(number) 函数中:在哈希表中添加 numbernumber 频数之间的映射关系。
  • find(value) 函数中:遍历哈希表,对于每个键值(number),我们检查哈希表中是否存在 value - number。如果存在,我们终止循环并返回结果。
  • number = value - number 时,在哈希表中 number 对应的值应大于 2

我们在下图中演示了算法:

170. 两数之和 III - 数据结构设计_第2张图片

class TwoSum(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.num_counts = {}


    def add(self, number):
        """
        Add the number to an internal data structure..
        :type number: int
        :rtype: None
        """
        if number in self.num_counts:
            self.num_counts[number] += 1
        else:
            self.num_counts[number] = 1

    def find(self, value):
        """
        Find if there exists any pair of numbers which sum is equal to the value.
        :type value: int
        :rtype: bool
        """
        for num in self.num_counts.keys():
            comple = value - num
            if num != comple:
                if comple in self.num_counts:
                    return True
            elif self.num_counts[num] > 1:
                return True
        
        return False
import java.util.HashMap;

class TwoSum {
  private HashMap<Integer, Integer> num_counts;

  /** Initialize your data structure here. */
  public TwoSum() {
    this.num_counts = new HashMap<Integer, Integer>();
  }

  /** Add the number to an internal data structure.. */
  public void add(int number) {
    if (this.num_counts.containsKey(number))
      this.num_counts.replace(number, this.num_counts.get(number) + 1);
    else
      this.num_counts.put(number, 1);
  }

  /** Find if there exists any pair of numbers which sum is equal to the value. */
  public boolean find(int value) {
    for (Map.Entry<Integer, Integer> entry : this.num_counts.entrySet()) {
      int complement = value - entry.getKey();
      if (complement != entry.getKey()) {
        if (this.num_counts.containsKey(complement))
          return true;
      } else {
        if (entry.getValue() > 1)
          return true;
      }
    }
    return false;
  }
}

复杂度分析

  • 时间复杂度:
    • add(number) 中: O ( 1 ) \mathcal{O}(1) O(1),只进行了哈希表的更新。
    • find(value) 中: O ( N ) \mathcal{O}(N) O(N),其中 N N N 指的是哈希表中键值对的数量。在最坏的情况下,会遍历整个表。
  • 空间复杂度: O ( N ) \mathcal{O}(N) O(N),哈希表所使用的空间大小。

你可能感兴趣的:(leetcode题解翻译)