贺志国
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
#include
#include
#include
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