C++标准库算法std::upper_bound示例

文章介绍了C++标准库中的std::upper_bound算法,该算法用于在升序排列的序列中找到第一个大于给定值的元素的迭代器位置。通过示例代码展示了如何使用该算法,并提供了一个自定义实现进行对比。此外,还包含了CMake构建系统和VSCode的调试配置信息。

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

C++标准库算法std::upper_bound示例

贺志国
2023.5.25

C++标准库算法std::upper_bound使用二分查找算法在已按升序排列的序列中返回指向第一个大于给定值的元素的迭代器,例如:已按升序排列的序列为{100.0, 101.5, 102.5, 102.5, 107.3},第一个大于83.2的元素为100.0,其索引值为0(起始序号为0),第一个大于102.5的元素为107.3,其索引值为4,大于110.2的值不存在。
std::upper_bound的声明如下:

// T类型提供大小比较语义,例如double
template< class ForwardIt, class T >
ForwardIt upper_bound( ForwardIt first, ForwardIt last, const T& value );

// T类型不提供大小比较语义,例如自定义类Person,因此需要提供一个自定义的
// Compare comp用于比较数据值的大小
template< class ForwardIt, class T, class Compare >
ForwardIt upper_bound( ForwardIt first, ForwardIt last,
                       const T& value, Compare comp );

以下是示例代码upper_bound.cpp

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

template <class ForwardIt, class T>
ForwardIt CustomUpperBound(ForwardIt first, ForwardIt last, const T& value) {
  ForwardIt iter;
  typename std::iterator_traits<ForwardIt>::difference_type count, step;
  count = std::distance(first, last);

  while (count > 0) {
    iter = first;
    step = count / 2;
    std::advance(iter, step);

    if (!(value < *iter)) {
      first = ++iter;
      count -= step + 1;
    } else {
      count = step;
    }
  }

  return first;
}

int main(int, char**) {
  std::vector<int> data = {1, 2, 4, 5, 5, 6};
  for (int i = 0; i < 7; ++i) {
    // Search first element that is greater than i
    auto upper = std::upper_bound(data.begin(), data.end(), i);
    auto custom_upper = CustomUpperBound(data.begin(), data.end(), i);
    if (upper != custom_upper) {
      std::cerr << "CustomUpperBound implementation is wrong. \n";
    }
    std::cout << i << " < ";
    upper != data.end() ? std::cout << *upper << " at index "
                                    << std::distance(data.begin(), upper)
                        : std::cout << " not found";
    std::cout << '\n';
  }

  std::vector<double> prices = {100.0, 101.5, 102.5, 102.5, 107.3};
  for (double target_val : {83.2, 102.5, 110.2}) {
    // Search first element that is greater than target_val
    auto upper = std::upper_bound(prices.begin(), prices.end(), target_val);
    auto custom_upper =
        CustomUpperBound(prices.begin(), prices.end(), target_val);
    if (upper != custom_upper) {
      std::cerr << "CustomUpperBound implementation is wrong. \n";
    }
    std::cout << target_val << " < ";
    upper != prices.end() ? std::cout << *upper << " at index "
                                      << std::distance(prices.begin(), upper)
                          : std::cout << " not found";
    std::cout << '\n';
  }

  return 0;
}

CMake编译文件CMakeLists.txt的内容如下:

cmake_minimum_required(VERSION 3.0.0)
project(upper_bound VERSION 0.1.0)

include(CTest)
enable_testing()

# If the debug option is not given, the program will not have debugging information.
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")

add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

VSCode的调试配置文件内容.vscode/launch.json如下:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "cpp_gdb_launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/build/${workspaceFolderBasename}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable neat printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            // "preLaunchTask": "cpp_build_task",
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}

编译命令如下:

mkdir build && cd build && cmake .. && make

如果build子目录已存在,则执行:

cd build && cmake .. && make

如果已在build子目录,则执行:

cmake .. && make

运行结果如下所示:

./upper_bound 
0 < 1 at index 0
1 < 2 at index 1
2 < 4 at index 2
3 < 4 at index 2
4 < 5 at index 3
5 < 6 at index 5
6 <  not found
83.2 < 100 at index 0
102.5 < 107.3 at index 4
110.2 <  not found

使用GDB调试的命令如下所示:

gdb -q ./upper_bound

调试截图如下所示:
gdb-debug

启动VSCode的调试界面如下图(调试快捷键与Visual Studio相同):
vscode-debug

### C++ `lower_bound` 和 `upper_bound` 的用法与区别 #### 函数定义 在C++标准库中,`std::lower_bound` 和 `std::upper_bound` 是用于二分查找的算法函数。它们通常被用来在一个已排序的序列中找到特定值的第一个位置或者最后一个位置之后的位置。 - **`std::lower_bound`**: 返回指向第一个不小于目标值的元素的迭代器[^4]。 - **`std::upper_bound`**: 返回指向上一个大于目标值的元素的迭代器[^5]。 这两个函数都要求输入容器中的数据是有序的(通常是升序)。如果未按顺序排列,则结果不可预测。 #### 参数说明 两者均接受四个参数: 1. 起始迭代器 (`first`):指定要搜索范围的起始地址。 2. 结束迭代器 (`last`):指定要搜索范围结束后的下一个地址。 3. 查找的目标值 (`value`):需要定位的具体数值。 4. 可选比较函数对象/谓词表达式(默认为 `<` 运算符)。 #### 使用示例 以下是两个函数的基本使用方法: ```cpp #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> vec = {1, 2, 4, 4, 5, 7}; int value_to_find = 4; auto lb_it = std::lower_bound(vec.begin(), vec.end(), value_to_find); if(lb_it != vec.end()) { std::cout << "Lower bound points to: " << *lb_it << '\n'; } else { std::cout << "Value not found\n"; } auto ub_it = std::upper_bound(vec.begin(), vec.end(), value_to_find); if(ub_it != vec.end()) { std::cout << "Upper bound points to: " << *ub_it << '\n'; } else { std::cout << "No element greater than the given value.\n"; } } ``` 在这个例子中,对于数组 `{1, 2, 4, 4, 5, 7}` 中寻找值 `4`, - `lower_bound` 将返回第一个等于或大于 `4` 的位置,即第二个 `4` 所处的位置; - `upper_bound` 则会跳过所有的 `4` 并停在紧随其后的更大数 `5` 上面[^6]。 #### 主要差异 | 特性 | `std::lower_bound` | `std::upper_bound` | |---------------------|---------------------------------------------|------------------------------------------| | 定义 | 第一个小于或等于给定值的位置 | 第一个严格大于给定值的位置 | | 时间复杂度 | O(log n) | O(log n) | 因此,在处理重复项时,`std::lower_bound` 经常用于获取某个键首次出现的地方;而 `std::upper_bound` 更适合确定该键最后一次出现之后的位置[^7]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值