面试之快速学习STL-常用算法

1. 排序算法

在这里插入图片描述

  1. sort() 函数基于快速排序实现的,故不保证相对位置,但是stable_sort (first, last)保证,它基于归并排序
  2. sort()只适用于支持随机迭代器的容器(array, vector, deque),好理解,毕竟用的快排
  3. 如果用默认的comp func排序,那么要支持 < (>) 重载
  4. 时间复杂度N*log2N
#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector
//以普通函数的方式实现自定义排序规则
bool mycomp(int i, int j) {
    return (i < j);
}
//以函数对象的方式实现自定义排序规则
class mycomp2 {
public:
    bool operator() (int i, int j) {
        return (i < j);
    }
};

int main() {
    std::vector<int> myvector{ 32, 71, 12, 45, 26, 80, 53, 33 };
    //调用第一种语法格式,对 32、71、12、45 进行排序
    std::sort(myvector.begin(), myvector.begin() + 4); //(12 32 45 71) 26 80 53 33
    //调用第二种语法格式,利用STL标准库提供的其它比较规则(比如 greater<T>)进行排序
    std::sort(myvector.begin(), myvector.begin() + 4, std::greater<int>()); //(71 45 32 12) 26 80 53 33
   
    //调用第二种语法格式,通过自定义比较规则进行排序
    std::sort(myvector.begin(), myvector.end(), mycomp2());//12 26 32 33 45 53 71 80
    //输出 myvector 容器中的元素
    for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) {
        std::cout << *it << ' ';
    }
    return 0;
}

2. 找第N小的数nth_element()

//排序规则采用默认的升序排序
void nth_element (RandomAccessIterator first,
                 RandomAccessIterator nth,
                 RandomAccessIterator last);
//排序规则为自定义的 comp 排序规则
void nth_element (RandomAccessIterator first,
                 RandomAccessIterator nth,
                 RandomAccessIterator last,
                 Compare comp);
  1. 运行完之后会出现一个现象是:该函数会找到第 n 大的元素 K 并将其移动到第 n 的位置处,同时所有位于 K 之前的元素都比 K 大,所有位于 K 之后的元素都比 K 小。
  2. 同样和大小有关,基本上需要随机访问迭代器啦,那么也需要重载运算符咯。
  3. 原理:其实就是快排一样的方法,只是找到第n个数就停止了

3. 选出前n个大的元素

有一个存有 100 万个元素的容器,但我们只想从中提取出值最小的 10 个元素,该如何实现呢?

//按照默认的升序排序规则,对 [first, last) 范围的数据进行筛选并排序
void partial_sort (RandomAccessIterator first,
                   RandomAccessIterator middle,
                   RandomAccessIterator last);
//按照 comp 排序规则,对 [first, last) 范围的数据进行筛选并排序
void partial_sort (RandomAccessIterator first,
                   RandomAccessIterator middle,
                   RandomAccessIterator last,
                   Compare comp);

1 . partial_sort() 函数会以交换元素存储位置的方式实现部分排序的。具体来说,partial_sort() 会将 [first, last) 范围内最小(或最大)的 middle-first 个元素移动到 [first, middle) 区域中,并对这部分元素做升序(或降序)排序
2. partial_sort采用的堆排序(heapsort),它在任何情况下的复杂度都是n*log(n). 如果你希望用partial_sort来实现全排序,你只要让middle=last就可以了。

4. 合适的排序算法

在这里插入图片描述

5. std::list排序,采用归并

参考下面的方法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    
    ListNode* merge(ListNode *head1,ListNode *head2) {
        ListNode *n_head=new ListNode(0);
        ListNode *h_head=n_head;
        while(head1!=nullptr && head2!=nullptr) {
            if(head1->val<head2->val) {
                ListNode *temp=head1->next;
                h_head->next=head1;
                head1=temp;
            } else if(head1->val>head2->val) {
                ListNode *temp=head2->next;
                h_head->next=head2;
                head2=temp;
            } else {
                ListNode *temp=head1->next;
                ListNode *temp1=head2->next;
                h_head->next=head1;
                h_head=h_head->next;
                h_head->next=head2;
                head1=temp;
                head2=temp1;
            }
            h_head=h_head->next;
        }
 
        if(head1!=nullptr) {
            h_head->next=head1;
        } else if(head2!=nullptr) {
            h_head->next=head2;
        } else h_head->next=nullptr;
        return n_head->next;
    }
    
    ListNode *helper(ListNode* head,ListNode *tail) {
        if(head==nullptr) return head;
        if(head->next==tail) {
            head->next=nullptr;
            return head;
        }
        ListNode *slow=head,*fast=head;
        while(fast!=tail) {
            slow=slow->next;
            fast=fast->next;
            if(fast!=tail) {
                fast=fast->next;
            }
        }
        ListNode *mid=slow;
        return merge(helper(head,mid),helper(mid,tail));
    }
    
    ListNode* sortList(ListNode* head) {
        return helper(head,nullptr);
    }
};


说明:
1.归并,找到中间值用的快慢指针
2. 但实际STL里面的排序用的是非递归的归并,代码可参考:
https://blog.csdn.net/qq_31720329/article/details/85535787
3. 如果对std::list做排序用std::sort,那么实际上随机访问数值时需要逐个获取,时间复杂度也相当于n^2了

6. 排序算法优先使用函数对象

性能优势
对于排序算法,使用函数对象编译器可以直接进行内联,减少函数调用次数。而使用普通函数时,传入算法内部的实际是函数指针,编译器无法对其进行优化。

7.合并两个有序的容器

借助 merge() 或者 inplace_merge() 函数实现。

//以默认的升序排序作为排序规则
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
                      InputIterator2 first2, InputIterator2 last2,
                      OutputIterator result);
//以自定义的 comp 规则作为排序规则
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
                      InputIterator2 first2, InputIterator2 last2,
                      OutputIterator result, Compare comp);
  1. 当 2 个有序序列存储在同一个数组或容器中时,如果想将它们合并为 1 个有序序列,除了使用 merge() 函数,更推荐使用 inplace_merge() 函数。

8. 查找

  1. find()函数就不说了
  2. 和 find() 函数相同,find_if() 函数也用于在指定区域内执行查找操作。不同的是,前者需要明确指定要查找的元素的值,而后者则允许自定义查找规则。
    std::vector<int> find_if_vec{4,2,3,1,5};
    auto find_if_it = std::find_if(find_if_vec.begin(), find_if_vec.end(), [](int a){return a%2 == 0;});
    std::cout << "find_if_it : " << *find_if_it << std::endl;
    /* find_if_it : 4 */
  1. find_if_not()相反
  2. find_end()和 search() :find_end() 函数用于在序列 A 中查找序列 B 最后一次出现的位置, 序列 B 在序列 A 中第一次出现的位置,该如何实现呢?可以借助 search() 函数。
  3. search() 和 search_n() :用于在指定区域内查找第一个符合要求的子序列。不同之处在于,前者查找的子序列中可包含多个不同的元素,而后者查找的只能是包含多个相同元素的子序列。

关于 search() 函数和 search_n() 函数的区别,给大家举个例子,下面有 3 个序列:

序列 A:1,2,3,4,4,4,1,2,3,4,4,4
序列 B:1,2,3
序列 C:4,4,4

如果想查找序列 B 在序列 A 中第一次出现的位置,就只能使用 search() 函数;而如果想查找序列 C 在序列 A 中第一次出现的位置,既可以使用 search() 函数,也可以使用 search_n() 函数。

9. 数据分类partition()和stable_partition()

  1. 我理解类似快排只是比较的不是大小,是一种条件
  2. partition 可直译为“分组”,partition() 函数可根据用户自定义的筛选规则,重新排列指定区域内存储的数据,使其分为 2 组,第一组为符合筛选条件的数据,另一组为不符合筛选条件的数据。
ForwardIterator partition (ForwardIterator first,
                           ForwardIterator last,
                           UnaryPredicate pred);
  1. partition() 函数还会返回一个正向迭代器,其指向的是两部分数据的分界位置,更确切地说,指向的是第二组数据中的第 1 个元素
#include <iostream>     // std::cout
#include <algorithm>    // std::partition
#include <vector>       // std::vector
using namespace std;
//以普通函数的方式定义partition()函数的筛选规则
bool mycomp(int i) { return (i % 2) == 0; }

//以函数对象的形式定义筛选规则
class mycomp2 {
public:
    bool operator()(const int& i) {
        return (i%2 == 0);
    }
};

int main() {
    std::vector<int> myvector{1,2,3,4,5,6,7,8,9};
    std::vector<int>::iterator bound;
    //以 mycomp2 规则,对 myvector 容器中的数据进行分组
    bound = std::partition(myvector.begin(), myvector.end(), mycomp2());
    for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) {
        cout << *it << " ";
    }
    cout << "\nbound = " << *bound;
    return 0;
}

10. 二分查找lower_bound()函数

  1. find()、find_if()、search() 等。值得一提的是,这些函数的底层实现都采用的是顺序查找(逐个遍历)的方式,在某些场景中的执行效率并不高.
  2. C++ STL标准库中还提供有 lower_bound()、upper_bound()、equal_range() 以及 binary_search() 这 4 个查找函数,它们的底层实现采用的都是二分查找的方式。
  3. lower_bound() 函数用于在指定区域内查找不小于目标值的第一个元素。也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。
  4. 注意这个不小于可以自己定义。
  5. 再次强调,该函数仅适用于已排好序的序列。所谓“已排好序”,指的是 [first, last) 区域内所有令 element<val(或者 comp(element,val),其中 element 为指定范围内的元素)成立的元素都位于不成立元素的前面。
std::vector<int> lower_bound_vec{4,5,3,1,2};
    auto lower_bound_vec_it = std::lower_bound(lower_bound_vec.begin(), lower_bound_vec.end(), 3, [](int i, int j) {return i >j; });
    std::cout<< "lower_bound_vec_it = " << *lower_bound_vec_it<<std::endl;
    /* 3 */
  1. 注意,myvector 容器中存储的元素看似是乱序的,但对于元素 3 来说,大于 3 的所有元素都位于其左侧,小于 3 的所有元素都位于其右侧,且查找规则选用的是 mycomp2(),其查找的就是第一个不大于 3 的元素,因此 lower_bound() 函数是可以成功运行的。

11. 二分查找upper_bound()函数

用于在指定范围内查找大于目标值的第一个元素。

举个例子,假设 有一个数组num:

1 2 2 2 3 4 5
value = 2
lower_bound 得到的是num[1]
uppper_bound得到的是nun[4]

12. 查找所有相等的元素

//找到 [first, last) 范围中所有等于 val 的元素
pair<ForwardIterator,ForwardIterator> equal_range (ForwardIterator first, ForwardIterator last, const T& val);

  1. 也要排好序
  2. 该函数会返回一个 pair 类型值,其包含 2 个正向迭代器。当查找成功时:
    第 1 个迭代器指向的是 [first, last) 区域中第一个等于 val 的元素;
    第 2 个迭代器指向的是 [first, last) 区域中第一个大于 val 的元素。
    反之如果查找失败,则这 2 个迭代器要么都指向大于 val 的第一个元素(如果有),要么都和 last 迭代器指向相同。
  3. 我可以理解为第一个返回的是lower_bound获取的值,第二个是upper_bound获取的值

13. 检查算法

  1. 用来检查在算法应用到序列中的元素上时,什么时候使谓词返回 true
  2. all_of() 算法会返回 true,前提是序列中的所有元素都可以使谓词返回 true。
  3. any_of() 算法会返回 true,前提是序列中的任意一个元素都可以使谓词返回 true。
  4. none_of() 算法会返回 true,前提是序列中没有元素可以使谓词返回 true。
    std::vector<int> none_of_vec{22, 19, 46, 75, 54, 19, 27, 66, 61, 33, 22, 19};
    std::cout << "There are "
    << (std::none_of(none_of_vec.begin(), none_of_vec.end(), [](const int &i){return i == 18;}) ? "no" : "some")
        << " student age = 18 ."
        << std::endl;
    /* There are no student age = 18 .*/
  1. 注意返回的是true or false
  2. count_if() 会返回可以使作为第三个参数的谓词返回 true 的元素个数。

14. copy_n(STL copy_n)算法详解

  1. copy_n() 算法可以从源容器复制指定个数的元素到目的容器中。第一个参数是指向第一个源元素的输入迭代器,第二个参数是需要复制的元素的个数,第三个参数是指向目的容器的第一个位置的迭代器
  2. 这个算法会返回一个指向最后一个被复制元素的后一个位置的迭代器
    std::vector<int> none_of_vec{22, 19, 46, 75, 54, 19, 27, 66, 61, 33, 22, 19};
    std::cout << "There are "
    << (std::none_of(none_of_vec.begin(), none_of_vec.end(), [](const int &i){return i == 18;}) ? "no" : "some")
        << " student age = 18 ."
        << std::endl;
    /* There are no student age = 18 .*/
    std::vector<std::string> names {"A1","George" ,"Harry", "Iain", "Beth","Carol",  "Eve","Dan", "Joe","Fred"};
    std::unordered_set<std::string> more_names {"Janet", "John"};
    std::copy_n(std::rbegin(names)+1, 3, std::inserter(more_names, std::begin(more_names)));
    
    /*
     [0] = "Eve"
     [1] = "Dan"
     [2] = "Joe"
     [3] = "John"
     [4] = "Janet"
     */
  1. 还有copy_if

后面还有很多算法不想看了,其实都是基本的算法
在这里插入图片描述

  1. unique() 算法可以在序列中原地移除重复的元素,这就要求被处理的序列必须是正向迭代器所指定的。在移除重复元素后,它会返回一个正向迭代器作为新序列的结束迭代器。可以提供一个函数对象作为可选的第三个参数,这个参数会定义一个用来代替 == 比较元素的方法。例如:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    remove是覆盖的方法和uinque一样

条件相等 replace
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/93424.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3d素材库素材资源平台大大节省老师备课时间

教育元宇宙相信大家有所耳闻&#xff0c;3D素材云库通过数字三维建模技术将现实中的物体、天气、灯光等1&#xff1a;1模拟还原到虚拟场景中&#xff0c;让人们在教育元宇宙平台中可视、可见、可感。 在元宇宙爆发的大背景下&#xff0c;3D互联网传播内容也将迎来一次全面升级&…

java八股文面试[java基础]——如何实现不可变的类

知识来源&#xff1a; 【23版面试突击】如何实现不可变的类&#xff1f;_哔哩哔哩_bilibili 【2023年面试】怎样声明一个类不会被继承&#xff0c;什么场景下会用_哔哩哔哩_bilibili

leetcode 567. 字符串的排列(滑动窗口-java)

滑动窗口 字符串的排列滑动窗口代码演示进阶优化版 上期经典 字符串的排列 难度 -中等 leetcode567. 字符串的排列 给你两个字符串 s1 和 s2 &#xff0c;写一个函数来判断 s2 是否包含 s1 的排列。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 换句…

Focal Loss-解决样本标签分布不平衡问题

文章目录 背景交叉熵损失函数平衡交叉熵函数 Focal Loss损失函数Focal Loss vs Balanced Cross EntropyWhy does Focal Loss work? 针对VidHOI数据集Reference 背景 Focal Loss由何凯明提出&#xff0c;最初用于图像领域解决数据不平衡造成的模型性能问题。 交叉熵损失函数 …

用AI重构的钉钉,“钱”路在何方?

点击关注 文&#xff5c;郝 鑫&#xff0c;编&#xff5c;刘雨琦 钉钉2023年生态大会&#xff0c;离开了两年的无招&#xff0c;遇到了单飞9天的钉钉。 “做小钉钉、做好钉钉、做酷钉钉”&#xff0c;无招重申了钉钉的方向。 无招提到的三点&#xff0c;再加上“高质量增长”…

Doris异常处理

1、decimal 字段异常 修改为 2、连接超时 Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure The last packet successfully received from the server was 1,068 milliseconds ago. The last packet sent successfully to the ser…

Redis限流实践:实现用户消息推送每天最多通知2次的功能

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

Docker数据管理(数据卷与数据卷容器)

目录 一、数据卷&#xff08;Data Volumes&#xff09; 1、概述 2、原理 3、作用 4、示例&#xff1a;宿主机目录 /var/test 挂载同步到容器中的 /data1 二、数据卷容器&#xff08;DataVolumes Containers&#xff09; 1、概述 2、作用 3、示例&#xff1a;创建并使用…

AIGC ChatGPT 实现动态多维度分析雷达图制作

雷达图在多维度分析中是一种非常实用的可视化工具,主要有以下优势: 易于理解:雷达图使用多边形或者圆形的形式展示多维度的数据,直观易于理解。多维度对比:雷达图可以在同一张图上比较多个项目或者实体在多个维度上的表现。数据关系明显:通过雷达图,可以直观的看出各个数…

C++贪吃蛇(控制台版)

C自学精简实践教程 目录(必读) 目录 主要考察 需求 输入文件 运行效果 实现思路 枚举类型 enum class 启动代码 输入文件data.txt 的内容 参考答案 学生实现的效果 主要考察 模块划分 文本文件读取 UI与业务分离 控制台交互 数据抽象 需求 用户输入字母表示方…

朋友圈也可以定时定量发送?

场景1&#xff1a;明天要搞活动&#xff0c;早中晚都得发朋友圈&#xff0c;一天要发3次朋友圈&#xff0c;要在手机上定好3个闹钟&#xff0c;这是一件非常麻烦的事。 场景2&#xff1a;有朋友是房产信息的&#xff0c;每天要发布很多二手房源&#xff0c;手动发圈太耗时间&a…

记录:yolov8训练自己的数据集

一、LabelImg标注自己的原图数据集 .xml标注格式 二、带标签的数据增强 先将原始数据&#xff08;图片&#xff0c;标注&#xff09;转移到项目根目录&#xff0c;然后再数据增强&#xff0c;避免标注内容路径错误。 亮度变换加旋转 # 一、亮度 img_dir multi/images # 原始…

CSS基础选择器及常见属性

文章目录 一、CSS1、CSS简介2、CSS语法规范 二、CSS基础选择器1、选择器的作用2、选择器分类3、基础选择器标签选择器类选择器id选择器通配符选择器 三、CSS常见属性1、字体属性字体系列字体大小字体粗细文字样式 2、文本属性文本颜色对齐文本装饰文本文本缩进行间距 四、CSS引…

python编写四画面同时播放swap视频

当代技术让我们能够创建各种有趣和实用的应用程序。在本篇博客中&#xff0c;我们将探索一个基于wxPython和OpenCV的四路视频播放器应用程序。这个应用程序可以同时播放四个视频文件&#xff0c;并将它们显示在一个GUI界面中。 C:\pythoncode\new\smetimeplaymp4.py 准备工作…

2023最新任务悬赏平台源码uniapp+Thinkphp新款悬赏任务地推拉新充场游戏试玩源码众人帮威客兼职任务帮任务发布分销机

新款悬赏任务地推拉新充场游戏试玩源码众人帮威客兼职任务帮任务发布分销机制 后端是&#xff1a;thinkphpFastAdmin 前端是&#xff1a;uniapp 1.优化首页推荐店铺模块如有则会显示此模块没有则隐藏。 2修复首页公告&#xff0c;更改首页公告逻辑。&#xff08;后台添加有公…

redis 6个节点(3主3从),始终一个节点不能启动

redis节点&#xff0c;始终有一个节点不能启动起来 1.修改了配置文件 protected-mode no&#xff0c;重启 修改了配置文件 protected-mode no&#xff0c;重启redis问题依然存在 2、查看/var/log/message的redis日志 Aug 21 07:40:33 redisMaster kernel: Out of memory: K…

Jumpserver堡垒机管理(安装和相关操作)-------从小白到大神之路之学习运维第89天

第四阶段 时 间&#xff1a;2023年8月28日 参加人&#xff1a;全班人员 内 容&#xff1a; Jumpserver堡垒机管理 目录 一、堡垒机简介 &#xff08;一&#xff09;运维常见背黑锅场景 &#xff08;二&#xff09;背黑锅的主要原因 &#xff08;三&#xff09;解决背黑…

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第三天)动态SQL

动态SQL—SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录&#xff08;第三天&#xff09;Mybatis的动态SQL操作 昨天我们深入学习了Mybatis的核心对象SqlSessionFactoryBuilder&#xff0c;掌握MyBatis核心配置文件以及元素的使用,也掌握My…

4-1-netty

非阻塞io 服务端就一个线程&#xff0c;可以处理无数个连接 收到所有的连接都放到集合channelList里面 selector是有事件集合的 对server来说优先关注连接事件 遍历连接事件

小研究 - Java虚拟机性能及关键技术分析

利用specJVM98和Java Grande Forum Benchmark suite Benchmark集合对SJVM、IntelORP,Kaffe3种Java虚拟机进行系统测试。在对测试结果进行系统分析的基础上&#xff0c;比较了不同JVM实现对性能的影响和JVM中关键模块对JVM性能的影响&#xff0c;并提出了提高JVM性能的一些展望。…