首页 > 编程语言 >【c++编程基础】std::unique的理解

【c++编程基础】std::unique的理解

时间:2024-12-31 10:42:06浏览次数:1  
标签:std float const center 元素 c++ unique

前言

项目中想要实现一个功能,对于一个自定义类,包含坐标和类别等属性,按照到某个中心点的角度从小到大排序,如果角度相同,只保留距离中心点更近的元素,过程中用到了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;
}

 

后记

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

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

参考

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

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

标签:std,float,const,center,元素,c++,unique
From: https://www.cnblogs.com/happyamyhope/p/18643323

相关文章

  • C++项目链接C语言动态库
     有C++项目B,有C语言动态链接库A,需要在B程序中链接A库。 我们知道C++运行环境可以直接运行C语言程序,但因为C++编译时对方法名的解析不同,所以要在C++项目中运行C语言程序,关键问题是需要告诉C++编译器,按照C语言的规范来编译指定的C代码。上面所述的“指定的C代码”,包括C++项目中......
  • 在 VC++ 里最大化并且前置窗口
    在VC++里最大化并且前置窗口在Windows系统中,如果需要通过编程的方式,前置显示另一个进程的某个窗口,你会发现,你遇到了一个麻烦。至少你会发现,仅仅使用SetForegroundWindow或SetWindowPos是没有效果的。下面是解决方案,在VC++2022、VC++2019,Window10和Wind......
  • 20. C++快速入门--并发基础
    参考:《Professionalc++》,《并发编程实战》1基本概念1.1竞争原子性"原子"(atomic)操作是指一种不可分割的操作,即在执行过程中不会被中断的操作。这种操作要么完全执行,要么完全不执行,不会出现部分执行的情况。应用场景计数器:在多线程环境下安全地递增或递减计数器。标......
  • C++引用
            目录1.引用的声明与初始化2.别名3.引用与指针的区别4.常量引用|const与引用5.引用作为函数参数6.引用返回值7.右值引用(C++11新特性)8.引用的实现        C++中的引用是一种特殊的类型,它是一个别名(或者叫作“引用名”),用来指向另一个变量。......
  • C++:正整数A+B
    正整数A+B题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000]。稍微有点麻烦的是,输入并不保证是两个正整数。输入格式:输入在一行给出A和B,其间以空格分开。问题是A和B不一定是满足要求的正整数,有时候可能是超出范围的数字、负数、带小数点的实数、甚至是一堆乱......
  • 21. C++快速入门--协程 Coroutine 入门
    参考:https://www.cnblogs.com/blizzard8204/p/17563217.htmlhttps://www.bennyhuo.com/2022/03/09/cpp-coroutines-01-intro/本文不完整,更新中1基本概念什么是协程?C++20的协程是一个特殊函数,具有挂起和恢复的能力.(可以不一次性执行)协程可用于异步编程,提供......
  • 16. C++快速入门--模板和Concept
    待修改1定义模板1.1模板形参模板参数模板可以有两种参数,一种是类型参数,一种是非类型参数这两种参数可以同时存在,非类型参数的类型可以是模板类型形参template< typenameT,//1 Ta//2>第一个参数是类型参数T第二个是非类型参数a,它的类型和形参......
  • C++语言知识点总结
    众所周知,C++有超级大量的相关知识,普通人类是难以做到“精通C++”的。不过,这不影响我想做一些C++语言知识点的总结,帮助自己理解C++的设计并更好地使用它:)一、基础概念相关类的构造关键点在于,对任何构造函数,C++会在进入构造函数体之前,自动调用所有成员变量的构造函数(如果未显式......
  • Effective C++ 条款40:明智而审慎地使用多重继承
    文章目录条款40:明智而审慎地使用多重继承示例1:多重继承中的歧义示例2:避免菱形继承问题示例3:合理使用多重继承总结条款40:明智而审慎地使用多重继承多重继承(MultipleInheritance)允许一个类从多个基类继承。然而,与单一继承相比,多重继承更加复杂,可能带来以下问题和......
  • C12.【C++ Cont】string类字符串的创建、输入、访问和size函数
    目录1.概念2.string字符串的创建示例代码 运行结果备注 2.string字符串的输入1.cin(字符串没有空格)2.getline(可以读含有空格的字符串)示例代码1:getline传两个参数示例代码2:getline传三个参数3.size函数示例代码运行结果4.访问string类字符串示例代码 运......