题目
编写程序,阐述 std::unique 的真实行为:验证其仅能去除“相邻的重复元素”,因此在去重前必须预先对容器进行 std::sort 排序。随后利用 sort + unique + erase + lower_bound 构建算法竞赛与工程几何中极其关键的“坐标离散化”(Coordinate Discretization)预处理流水线,将大范围稠密/稀疏坐标值映射为紧凑的整数排名下标。
输入格式:无需外部输入。
输出格式:输出原始极大值坐标、离散化去重结果与映射后的压缩排名索引。
数据范围与约定:坐标离散化、相邻去重与二分投影。
思路与算法
1. unique 的相邻性陷阱
如果数组是 {1, 2, 1},调用 unique 毫无作用,因为两个 1 不相邻!必须先 std::sort 将所有相同元素聚拢,然后再调用 unique。
2. 离散化的应用背景
当坐标数值很大(如 \(x \in [-10^9, 10^9]\)),无法直接建立数组当下标;若只关心相对大小关系,将其保序离散化映射到 \([0, N-1]\) 可以大幅降低空间占用。
3. lower_bound 寻址映射
完成 sort + unique + erase 后的序列严格单调递增,查询原数字 \(x\) 映射后的编号只需执行 std::lower_bound(v.begin(), v.end(), x) - v.begin()。
完整程序
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// 原始具有极大跨度与重复项的坐标列表
std::vector<int> coords = {1000000000, -500, 42, -500, 1000000000, 0, 42};
std::cout << "原始离散坐标集合: ";
for (int x : coords) std::cout << x << " ";
std::cout << "\n";
// 1. 拷贝并构建纯净且去重的保序基准表
std::vector<int> discreteTable = coords;
// 关键第一步:必须先排序将重复项聚拢到相邻位置
std::sort(discreteTable.begin(), discreteTable.end());
// 关键第二步:调用 unique 消除相邻重复项,并截断多余尾部
discreteTable.erase(
std::unique(discreteTable.begin(), discreteTable.end()),
discreteTable.end()
);
std::cout << "排序去重后的映射基准表: ";
for (int x : discreteTable) std::cout << x << " ";
std::cout << " (基准元素数: " << discreteTable.size() << ")\n\n";
// 2. 将原序列中的每个大数投影转换为离散紧凑下标 [0, K-1]
std::cout << "--- 原始坐标在离散表中的紧凑下标映射结果 ---\n";
for (int x : coords) {
auto it = std::lower_bound(discreteTable.begin(), discreteTable.end(), x);
int rankIndex = std::distance(discreteTable.begin(), it);
std::cout << "坐标 " << x << " -> 离散编号: [" << rankIndex << "]\n";
}
return 0;
}运行示例
输出:
原始离散坐标集合: 1000000000 -500 42 -500 1000000000 0 42
排序去重后的映射基准表: -500 0 42 1000000000 (基准元素数: 4)
--- 原始坐标在离散表中的紧凑下标映射结果 ---
坐标 1000000000 -> 离散编号: [3]
坐标 -500 -> 离散编号: [0]
坐标 42 -> 离散编号: [2]
坐标 -500 -> 离散编号: [0]
坐标 1000000000 -> 离散编号: [3]
坐标 0 -> 离散编号: [1]
坐标 42 -> 离散编号: [2]