算法系列06——加强堆(手动改写堆)

文章讨论了如何利用反向索引表优化线段重合问题的解决方案,通过建立带反向索引的小根堆,可以更高效地处理线段的开始和结束位置,从而在O(logN)的时间复杂度内完成修改和查找操作。在面试中遇到的大数据量限制下,这种优化显得尤为重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 建立反向索引表
  • 建立比较器
  • 核心在于各种结构相互配合
  • 最大线段重合问题
    • 题目详细
      • 每一个线段的开始和结束位置都是整数
      • 重合区域长度大于等于1才叫重合
      • 返回一个重合区域,使区域上的重合线段数量最多是多少
    • 第一种暴力
      • 先找最小的开始位置和最大的结束位置
      • 考察这个区域上的每一个点5,因为一个重合区域必然包含一个点5
      • 算出每一个点5的位置被多少的线段覆盖,取最大值
      • 复杂度是(max - min) * N,受制于max-min
    • 好办法
      • 根据开始位置从小到大将所有线段排序
      • 准备一个小根堆
      • 从排好序的数组种依此取出线段
        • 看这个线段的开始,小根堆种弹出比这个开始小于等于的数
        • 看这个线段的结束,小根堆中压入这个结束
        • 记录当前堆中留下的数的个数
          • 含义是以当前线段的开始为左边界,越过这里的有几条线段
      • 记录下的留下的数的个数中最大的那个就是答案
    • 关于理解
      • 每一个重合部分的左边界,都是某个线段的左边界

面试中的数据量限制

  • 一般c++给1-2s
  • java给2-4s
  • 指的是代码条数在108-109,一般控制指令条数在108

加强堆

  • 对于一个堆来说,如果改变堆中某一个位置的值,这个堆就变成了无效堆
  • 如果这个值比原来大,应该要heapify,如果比原来小,应该要heapinsert
  • 但是系统实现的堆不支持这种操作
  • 系统不能高效的完成这样的操作,是因为系统建立的堆没有反向索引表
    • 数组本身代表一张索引表,从下标索引到存储的元素
    • 但是不能从元素映射回下标位置,只能遍历找到修改了值的那个元素
  • 自己加反向索引表就能做到logN的代价
  • c++委员会会提供带反向索引的堆,但偏实用的包中不会提供
  • 反向索引表的写法,在堆中增加一个hashmap,indexMap
  • arryList的get和set是O(1)复杂度
  • 目前的模板是非基础类型,有基础类型需求就要包一层,这是hashmap的设计原因
  • 将反向索引表的修改直接封装到交换操作里,保证反向索引表与动态数组同步
  • 弹出的时候要反向索引表删除这个弹出的值,压入的时候反向索引表也要增加这个值
  • 修改了某一个值就是顺序进行heapInsert,heapify,因为值要么变大要么变小,实际上只会执行二者之一,logn就能完成修改某一个值,非常高效
  • 还可以实现高效的删除某一个值,系统只能删除堆顶
    • 首先找到这个要删除的值在哪里
    • 然后用堆最后一个元素与这个值进行交换,删掉最后一个,替换后的位置上顺序进行heapInsert,heapify
### 深度优先搜索(DFS)算法的实现与原理 #### 一、DFS算法的核心思想 深度优先搜索(Depth-First Search, DFS)是一种用于遍历或搜索树或图的算法。其核心思想是从某个起点出发,沿着一条路径尽可能深入地探索下去,直到无法再前进时才回退到上一步,并尝试另一条可能的路径[^1]。 该方法通常采用递归或者显式的栈结构来辅助完成。为了防止无限循环以及重复访问同一节点,在执行过程中需要记录已经访问过的节点状态[^2]。 #### 二、DFS算法的具体流程 以下是DFS的主要操作步骤描述: 1. **初始化**:设置初始条件,比如指定起始顶点,并创建一个布尔数组用来跟踪哪些顶点已经被访问。 2. **递归调用/压入栈**:对于当前处理的顶点,先将其标记为已访问;接着依次检查它的邻接点是否尚未被访问过——如果是的话就进入这些新的未探查区域继续向下挖掘直至尽头。 3. **返回机制**:一旦到达死胡同即无路可走的状态下,则应回撤至上一层级重新寻找其他出路。 4. **终止条件**:当所有的连通部分均已完成扫描之后结束整个过程[^3]。 #### 三、DFS算法的两种主要实现方式 ##### (1)基于递归的方法 这是最常见也是最容易理解的一种形式。下面给出一段简单的 Python 实现代码: ```python def dfs_recursive(graph, node, visited): if node not in visited: print(node, end=' ') visited.add(node) for neighbor in graph[node]: if neighbor not in visited: dfs_recursive(graph, neighbor, visited) # Example usage of recursive DFS function if __name__ == "__main__": adj_list = { 'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['F'], 'D': [], 'E': ['F'], 'F': [] } start_node = 'A' visited_nodes = set() dfs_recursive(adj_list, start_node, visited_nodes) ``` 上述函数定义了一个`dfs_recursive()`来进行递归式DFS遍历。它接受三个参数分别是图形表示(`graph`)、当前正在考察的节点名(`node`)还有存储所有曾经造访过得地点集合对象(`visited`)。 ##### (2)利用栈的数据结构模拟非递归版本 有时候为了避免潜在的大规模嵌套带来的风险或者是出于个人喜好考虑,可以改写成迭代版的形式如下所示: ```python def dfs_iterative(graph, start): stack, path = [start], [] while stack: vertex = stack.pop() # Pop from top of the stack. if vertex in path: continue path.append(vertex) print(vertex, end=" ") # Add all adjacent vertices to the front of the queue (stack). for neighbor in reversed(sorted(graph[vertex])): stack.append(neighbor) # Example Usage if __name__ == '__main__': adjacency_dict = {'A' : ['B','S'], 'B' : ['A'], 'S' : ['G','C'], 'D' : ['C'], 'G' : ['H','F'], 'H' : [], 'E' : ['D'], 'F' : ['C'], 'C' : ['D','E','F']} source_vertex = 'A' result_path = dfs_iterative(adjacency_dict,source_vertex) ``` 这里我们手动维护了一个列表当作LIFO行为模式下的工作区—也就是常说的那个“栈”。每次取出顶部元素进行扩展之前都会确认一下是不是已经在最终结果里出现过了,以此达到去重的目的。 #### 四、典型的应用场景举例说明 除了基本功能外,DFS还可以广泛应用于解决实际生活中的许多复杂难题之中,例如但不限于以下几个方面: - 图形学领域内的拓扑排序问题; - 寻找两个特定位置之间是否存在任何连接通道的任务; - 探索迷宫出口路线规划等等^。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值