博客
关于我
POJ 2892 Tunnel Warfare(树状数组+二分)
阅读量:793 次
发布时间:2023-03-03

本文共 2958 字,大约阅读时间需要 9 分钟。

二分求上界和下界,树状数组的巧妙应用

在算法学习中,二分查找是一种常用的高效方法,尤其在处理大量数据时,它能够快速缩小数据范围,找到目标值。然而,在实际应用中,二分查找往往需要配合数据结构来加速操作,而树状数组(Fenwick Tree)正是这种场景下的理想选择。以下将详细讲解二分查找的上界和下界问题,以及如何利用树状数组来高效解决这些问题。

二分查找的基本原理

二分查找是一种基于比较操作的查找算法,其核心思想是将搜索范围不断缩小。对于一个有序数组,二分查找通过比较中间元素来决定搜索方向,最终找到目标值。二分查找的时间复杂度为O(log n),这使得它在处理大数据量时非常高效。

在二分查找中,我们常常需要确定一个目标值的上界和下界。例如,给定一个数组,寻找大于某个值的最小元素,或者小于某个值的最大元素。这些问题可以通过二分查找来解决,而树状数组则为这些操作提供了更高效的实现方案。

树状数组的实现原理

树状数组是一种基于二进制的前缀和数据结构,能够在O(log n)时间内完成更新和查询操作。它通过将数组索引转换为二进制形式,并利用每个位表示某个区间的贡献,从而实现快速操作。

树状数组的主要操作包括:

  • 插入操作:将一个值插入到数组中,并更新其前缀和。
  • 前缀和查询:获取某个索引的前缀和。
  • 更新操作:单点更新数组中的某个值,并更新其前缀和。
  • 二分求上界和下界的实现

    在本文中,我们将结合树状数组,通过二分查找的方法,实现上界和下界的查找操作。以下是具体实现的步骤:

    上界查找

  • 目标:在一个递增的数组中,找到第一个大于目标值x的元素。
  • 初始范围:设置初始搜索范围为1到n。
  • 二分查找
    • 计算中间点mid。
    • 获取mid位置的前缀和sum。
    • 如果sum > x,说明mid位置的元素可能是目标元素的上界,调整搜索范围。
    • 否则,继续搜索右侧范围。
  • 返回结果:当搜索范围缩小到单个元素时,即为上界。
  • 下界查找

  • 目标:在一个递增的数组中,找到第一个小于等于目标值x的元素。
  • 初始范围:设置初始搜索范围为1到n。
  • 二分查找
    • 计算中间点mid。
    • 获取mid位置的前缀和sum。
    • 如果sum < x,说明mid位置的元素可能不是下界,调整搜索范围。
    • 否则,继续搜索左侧范围。
  • 返回结果:当搜索范围缩小到单个元素时,即为下界。
  • 树状数组的优化与应用

    树状数组在实现二分查找时,能够显著提高查找效率。通过利用前缀和的快速查询和更新,树状数组将原本O(n)的时间复杂度,降低到O(log n)。这使得在处理大规模数据时,能够以更高效的速度完成任务。

    代码示例

    以下是实现二分查找与树状数组的C++代码:

    #include 
    #include
    #include
    #include
    #include
    #include
    using namespace std; int p[100001]; int o[100001]; int stack[100001]; int n; int lowbit(int t) { return t & (-t); } void insert(int t, int d) { while (t <= n) { p[t] += d; t += lowbit(t); } } int getsum(int t) { int sum = 0; while (t > 0) { sum += p[t]; t -= lowbit(t); } return sum; } int fr(int x) { int str, end, temp, mid; str = 1; end = n; while (str < end) { mid = (str + end + 1) / 2; temp = getsum(mid); if (temp > x) { end = mid - 1; } else { str = mid; } } return end; } int fl(int x) { int str, end, temp, mid; str = 1; end = n; while (str < end) { mid = (str + end) / 2; temp = getsum(mid); if (temp < x) { str = mid + 1; } else { end = mid; } } return str; } int main() { int i, m, top, num, t; char ch[12]; while (scanf("%d%d", &n, &m) != EOF) { top = 0; for (i = 1; i <= n; i++) { p[i] = 0; o[i] = 0; } for (i = 1; i <= m; i++) { scanf("%s", ch); if (ch[0] == 'R') { if (top != 0) { o[stack[top--]] = 0; insert(stack[top--], -1); } } else if (ch[0] == 'D') { scanf("%d", &num); stack[++top] = num; if (o[num] == 0) { o[num] = 1; insert(num, 1); } } else if (ch[0] == 'Q') { scanf("%d", &num); if (o[num]) { printf("0\n"); } else { t = getsum(num); if (t == 0) { printf("%d\n", fr(t)); } else { printf("%d\n", fr(t) - fl(t)); } } } } } return 0; }

    代码解释

  • 数据结构初始化:包括树状数组p和o,以及操作栈stack。
  • lowbit函数:计算当前索引的最低有效位,用于树状数组的更新操作。
  • insert函数:将一个值插入到树状数组中,并更新其前缀和。
  • getsum函数:查询前缀和。
  • fr函数:实现上界查找。
  • fl函数:实现下界查找。
  • main函数:处理输入并执行各种操作。
  • 应用案例

    假设我们有一个数组,元素按递增顺序排列。对于一个给定的目标值x,我们可以使用上述代码来快速找到其上界和下界。例如:

    • 上界:找到第一个大于x的元素。
    • 下界:找到最后一个小于等于x的元素。

    注意事项

  • 树状数组的大小需要根据实际需求进行调整。
  • 二分查找的实现需要确保数组是有序的。
  • 在实际应用中,可能需要对查询结果进行额外的处理,如重复值的处理等。
  • 总结

    通过结合二分查找和树状数组,我们能够在O(log n)时间内高效地找到数组的上界和下界。这一技术在处理大规模数据时表现出色,能够显著提升算法的效率。如果你对树状数组或二分查找感兴趣,可以进一步深入研究它们的内部原理和更多应用场景。

    转载地址:http://wfxfk.baihongyu.com/

    你可能感兴趣的文章
    PHP获取curl传输进度
    查看>>
    PHP获取IP所在地区(转)
    查看>>
    PHP获取IP的方法对比
    查看>>
    php获取json里面内容
    查看>>
    R2的版本由来
    查看>>
    PHP获取图片宽度高度、大小尺寸、图片类型、用于布局的img属性
    查看>>
    PHP获取当前时间、时间戳的各种格式写法汇总
    查看>>
    PHP获取当前页面的完整URL
    查看>>
    php获取文件夹中文件的两种方法
    查看>>
    PHP获取日期的一些方法总结
    查看>>
    R2学习记录
    查看>>
    PHP获取本周的每一天的时间
    查看>>
    php获取网页内容的三种方法
    查看>>
    R-CNN算法优化策略
    查看>>
    PHP规范PSR0和PSR4的理解
    查看>>
    php解析ipa包,获取logo
    查看>>
    php设置socket超时时间
    查看>>
    php设计模式 萨莱 pdf,PHP设计模式 建造者模式
    查看>>
    php设计模式之装饰器模式
    查看>>
    R&Python Data Science系列:数据处理(5)--字符串函数基于R(一)
    查看>>