惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
H
Heimdal Security Blog
罗磊的独立博客
B
Blog RSS Feed
WordPress大学
WordPress大学
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
美团技术团队
量子位
GbyAI
GbyAI
Recent Announcements
Recent Announcements
博客园 - 叶小钗
D
DataBreaches.Net
S
SegmentFault 最新的问题
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Troy Hunt's Blog
The Last Watchdog
The Last Watchdog
O
OpenAI News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
Webroot Blog
Webroot Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
V
V2EX
N
News and Events Feed by Topic
Jina AI
Jina AI
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
IT之家
IT之家
C
Check Point Blog
H
Hacker News: Front Page
爱范儿
爱范儿
Schneier on Security
Schneier on Security
Apple Machine Learning Research
Apple Machine Learning Research
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 最新话题
Forbes - Security
Forbes - Security
人人都是产品经理
人人都是产品经理
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
The Cloudflare Blog
Simon Willison's Weblog
Simon Willison's Weblog
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
MongoDB | Blog
MongoDB | Blog
V2EX - 技术
V2EX - 技术
AI
AI

博客园 - 鹅要长大

【c++编程基础】理解lambda表达式 【c++编程基础】字节对齐pack的理解 【c++编程基础】虚函数virtual的理解与使用 【linux基础知识】理解nohup 【linux基础】如何理解python train_dtld.py 2>&1 | tee my_error_log.txt 【工具使用】docker的使用 【基础算法】银行家舍入法 【ubuntu工具】ubuntu好用工具总结 【ubuntu基础工具使用】ubuntu中screen的安装与使用 【linux操作基础】多目录文件复制且同名不覆盖 【linux基础操作】windows系统生成的txt文件在ubuntu打开乱码 【opencv编程基础】fillpoly和polylines函数的理解 【编程基础】计算三个顶点之间的夹角 【计算几何算法】道格拉斯普克(Douglas-Peuker)算法 [c++]c++ 工程代码中的debug时条件编译隐去的代码会影响程序运行的性能和耗时吗 【CV基础】语义分割任务计算类别权重 【opencv基础】resize使用的问题 git stash的用法 gflags的使用 opencv 判断某个坐标点是否在多边形内cv::pointPolygonTest 判断多边形的顶点内外角点以及对approxPolyDP函数的理解 用Python从零实现贝叶斯分类器 【CV数据集】智慧城市之CCPD车牌数据集
【c++编程基础】std::unique的理解
鹅要长大 · 2024-12-31 · via 博客园 - 鹅要长大

前言

项目中想要实现一个功能,对于一个自定义类,包含坐标和类别等属性,按照到某个中心点的角度从小到大排序,如果角度相同,只保留距离中心点更近的元素。编程实现过程中用到了0-360的角度计算,自定义函数排序,以及删除重复元素等内容,故记录之。

具体内容

1. 计算到中心点的角度;

// 计算点到中心点的角度(0-360度)
float calculateAngle(const cv::Point& center, const cv::Point& point) {
    cv::Point vec = point - center;
    float angle = atan2f(vec.y, vec.x) * 180.0f / CV_PI; // 将弧度转换为角度
    if (angle < 0) {
        angle += 360.0f; // 确保角度在0-360度范围内
    }
    return angle;
}

2. 比较函数,角度从小到大排序,如果角度相同,保留距离近的,舍弃距离远的;

bool compareByAngle(const Fish2IpmPot& p1, const Fish2IpmPot& p2, const cv::Point& center) {
    float angle1 = calculateAngle(center, p1.coord);
    float angle2 = calculateAngle(center, p2.coord);
    // if(std::abs(angle1-angle2) < ANGLE_TOLERANCE){
    if(angle1 == angle2){
        float dist1 = cv::norm(p1.coord - center);
        float dist2 = cv::norm(p2.coord - center);
        return dist1 < dist2;
    }
    return angle1 < angle2;
}

3. 对above区域的fs进行排序;

void sortAbove(std::vector<Fish2IpmPot>& pts){
    // 180-360, 与x轴的夹角,顺时针
    std::sort(pts.begin(), pts.end(), [](const Fish2IpmPot& p1, const Fish2IpmPot& p2){
        return compareByAngle(p1, p2, a_center);
    });    
    // 去重:舍弃角度相同但距离较远的点
    auto last = std::unique(pts.begin(), pts.end(), [a_center](const Fish2IpmPot& p1, const Fish2IpmPot& p2) {
        float angle1 = calculateAngle(a_center, p1.coord);
        float angle2 = calculateAngle(a_center, p2.coord);
        return angle1 == angle2; // 如果角度相同,认为是重复点
    });
    // 删除重复点
    pts.erase(last, pts.end());
}

4. 对std::unique的理解;

1) std::unique函数,用于去除容器中相邻重复元素,连续重复的元素只保留最前面的元素;

特别需要强调和注意的是,

a)只有当遇到连续重复的元素,才会起到去重作用,如果容器中存在非相邻的重复元素,则无法去除。比如{1,2,1},则unique不能对其进行去重;

b) unique操作不会改变容器的大小,它只是将保留下来的元素从头一一覆盖到容器中去,多出来的部分依旧是可以访问的,这部分元素需要自行删除;

2)unique是稳定的,去重后,剩余元素的顺序是不变的;

3) 原型定义在algorithm头文件;

4) unique有两个方法,一个是使用默认的方式来定义重复元素,另一种是以自定义方式来判断是否是重复元素;

[first, last)表示容器中需要去重的范围,返回值是一个前向指针,指向去重后的最后一个有效元素的下一个位置iter,可以通过删除[iter, end),保留[first,iter)达到去重效果;

a) 默认方式

默认方式: 如果有连续的元素都满足彼此相等或者指向的对象相等,则为重复元素;
template <class ForwardIterator>
ForwardIterator unique(ForwardIterator first, ForwardIterator last);

b) 自定义方式

自定义方式:如果有连续的元素都满足彼此相等,或者指向的对象在自定义的二元谓词的作用下依旧为true即binary_pred(*i, *(i-1))==true,则为重复元素;
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate binary_pred);

5) 由于unique只能对相邻的连续的重复元素去重,一般需要先进行排序,采用sort和unique的方式进行去重,同时使用erase删除迭代器之后的无效元素;

6) unique的时间复杂度O(N),空间复杂度是O(1);sort的时间复杂度平均是O(NlogN),最坏是O(N*N),空间复杂度O(logN);

7) 简单示例

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

int main() {
    std::vector<int> vec = {1, 1, 2, 3, 3, 4, 5, 5, 6};
    auto new_end = std::unique(vec.begin(), vec.end());
    std::cout << "Vector after unique: " << vec.size() << std::endl;
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl << "Vector from begin to new_end: " << vec.size() << std::endl;;
    for (auto it = vec.begin(); it != new_end; ++it) {
        std::cout << *it << " ";
    }
    vec.erase(new_end, vec.end());
    std::cout << std::endl << "Vector after erase: " << vec.size() << std::endl;
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }    
    return 0;
}

output:

Vector after unique: 9
1 2 3 4 5 6 5 5 6 
Vector from begin to new_end: 9
1 2 3 4 5 6 
Vector after erase: 6
1 2 3 4 5 6  

后记

今天是2024年的最后一天,明天元旦。

祝自己,平安喜乐、万事顺遂、外儒内法、拓展认知边界、提升职场能力、永葆赤子之心!

参考

1. unique()函数_unique() 函数-CSDN博客

2. STL 官网学习笔记—— unique_std::unique会改变元素顺序吗-CSDN博客