返回

深入探索二分搜索的艺术:解析LeetCode第2276题(Python实现)

后端

问题概述

LeetCode第2276题要求我们统计一个给定数组中,有多少个整数位于一组指定区间的范围内。这组区间可能会有所重叠,但数组中的每个整数只会属于一个区间。

算法选择

要解决这个问题,我们可以使用两种主要算法:二分搜索和珂朵莉树算法。

二分搜索

二分搜索是一种经典的搜索算法,用于在有序列表中快速找到目标值。我们可以使用二分搜索来找到每个区间中的第一个和最后一个整数。然后,我们可以计算出每个区间包含的整数数量。

珂朵莉树算法

珂朵莉树算法(又称老司机算法)是一种专门为处理有序区间而设计的数据结构。它可以快速找到区间中的最大值和最小值,也可以快速统计出区间中整数的数量。

Python实现

from bisect import bisect_left, bisect_right

def count_integers(intervals, queries):
  """
  统计区间内的整数数量

  Args:
    intervals (list): 有序区间列表
    queries (list): 查询列表

  Returns:
    list: 查询结果列表
  """

  # 使用珂朵莉树算法构建线段树
  tree = SegmentTree(intervals)

  # 存储查询结果
  result = []

  # 遍历查询列表
  for query in queries:
    # 使用二分搜索找到区间的第一个和最后一个整数
    left_index = bisect_left(intervals, query[0])
    right_index = bisect_right(intervals, query[1])

    # 使用珂朵莉树算法统计区间中的整数数量
    count = tree.query(left_index, right_index - 1)

    # 将统计结果添加到查询结果列表中
    result.append(count)

  return result

# 线段树类
class SegmentTree:
  def __init__(self, intervals):
    """
    线段树构造函数

    Args:
      intervals (list): 有序区间列表
    """

    # 初始化线段树的节点数量
    self.n = len(intervals)

    # 初始化线段树的根节点
    self.root = self._build_tree(intervals, 0, self.n - 1)

  def _build_tree(self, intervals, start, end):
    """
    递归构建线段树

    Args:
      intervals (list): 有序区间列表
      start (int): 线段树节点的起始索引
      end (int): 线段树节点的结束索引

    Returns:
      SegmentTreeNode: 线段树的节点
    """

    # 如果当前节点是叶节点,则直接返回叶节点
    if start == end:
      return SegmentTreeNode(intervals[start])

    # 计算当前节点的左右子节点的索引范围
    mid = (start + end) // 2
    left_start, left_end = start, mid
    right_start, right_end = mid + 1, end

    # 递归构建线段树的左右子节点
    left_child = self._build_tree(intervals, left_start, left_end)
    right_child = self._build_tree(intervals, right_start, right_end)

    # 创建当前节点并返回
    node = SegmentTreeNode(None)
    node.left = left_child
    node.right = right_child

    return node

  def query(self, start, end):
    """
    查询区间内的整数数量

    Args:
      start (int): 查询区间的起始索引
      end (int): 查询区间的结束索引

    Returns:
      int: 区间内的整数数量
    """

    # 使用递归查询线段树
    return self._query(self.root, start, end)

  def _query(self, node, start, end):
    """
    递归查询线段树

    Args:
      node (SegmentTreeNode): 当前节点
      start (int): 查询区间的起始索引
      end (int): 查询区间的结束索引

    Returns:
      int: 区间内的整数数量
    """

    # 如果查询区间完全包含当前节点的区间,则直接返回当前节点的值
    if start <= node.start and end >= node.end:
      return node.value

    # 如果查询区间与当前节点的区间没有交集,则直接返回0
    if start > node.end or end < node.start:
      return 0

    # 递归查询线段树的左右子节点
    left_count = self._query(node.left, start, end)
    right_count = self._query(node.right, start, end)

    # 返回左右子节点的统计结果之和
    return left_count + right_count

# 线段树节点类
class SegmentTreeNode:
  def __init__(self, value):
    """
    线段树节点构造函数

    Args:
      value: 节点的值
    """

    # 节点的值
    self.value = value

    # 节点的左右子节点
    self.left = None
    self.right = None

    # 节点的起始索引和结束索引
    self.start = None
    self.end = None

结语

在本文中,我们探讨了如何使用二分搜索和珂朵莉树算法来解决LeetCode第2276题。我们还提供了清晰的Python实现,帮助您轻松理解算法的细节。希望这篇文章对您有所帮助,如果您有任何问题或建议,欢迎随时提出。