sort等常用方法和技巧
sort等常用方法和技巧
sort
sort(first_pointer,first_pointer+n,cmp)
原理:sort并不是简单的快速排序,它对快速排序进行了优化。此外,它还结合了插入排序和堆排序。系统会根据数据形式和数据量自动选择合适的排序方法。它每次排序中不只选择一种方法,比如给一个数据量较大的数组排序,开始采用快速排序,分段递归,分段之后每一段的数据量达到一个较小值后它就不继续往下递归,而是选择插入排序,如果递归的太深,他会选择推排序。
- 3个参数介绍
参数1:第一个参数是数组的首地址,一般是数组名或者迭代器。
参数2:要排序数据的尾地址。
参数3:默认可以不填,如果不填sort会默认按数组升序排序。可以自定义一个排序函数,改排序方式为降序。
sort传的前两个参数是前闭后开的
sort拓展:cmp比较函数自定义
- 定义比较函数
// 情况一:数组排列
int A[100];
bool cmp1(int a,int b)//int为数组数据类型
{
return a>b;//降序排列
//return a<b;//默认的升序排列
}
sort(A,A+100,cmp1);
// 情况二:结构体排序
Student Stu[100];
bool cmp2(Student a,Student b)
{
return a.id>b.id;//按照学号降序排列
}
sort(Stu,Stu+100,cmp2);
- 标准库函数
functional提供了一堆基于模板的比较函数对象:equal_to、not_equal_to、greater、greater_equal、less、less_equal。
● 升序:sort(begin,end,less())
● 降序:sort(begin,end,greater())
缺点:也只是实现简单的排序,结构体不适用。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <functional>
using namespace std;
//简单使用方法
sort(A,A+100,greater<int>());//降序排列
sort(A,A+100,less<int>());//升序排列
- 重载结构体或类的比较运算符
元素本身为class或者struct,类内部需要重载< 运算符,实现元素的比较;
注意事项:bool operator<(const className & rhs) const; 如何参数为引用,需要加const,这样临时变量可以赋值;重载operator<为常成员函数,可以被常变量调用;
#include<iostream>
#include<algorithm>
#include"vector"
using namespace std;
typedef struct student{
char name[20];
int math;
//按math从大到小排序
inline bool operator < (const student &x) const {
return math>x.math ;
}
}Student;
int main(){
Student a[4]={{"apple",67},{"limei",90},{"apple",90}};
sort(a,a+3);
for(int i=0;i<3;i++) cout<<a[i].name <<" "<<a[i].math <<" " <<endl;
return 0;
}
// 在结构体外重载时
struct Cmp{
bool operator()(Info a1, Info a2) const{
return a1.val > a2.val;
}
}
匿名函数
参考文章:深入浅出 C++ Lambda表达式:语法、特点和应用_c++lambda表达式作为函数参数的用法-CSDN博客
[capture list] (parameter list) -> return type { function body }
capture list
是捕获列表,用于指定 Lambda表达式可以访问的外部变量,以及是按值还是按引用的方式访问。捕获列表可以为空,表示不访问任何外部变量,也可以使用默认捕获模式&
或=
来表示按引用或按值捕获所有外部变量,还可以混合使用具体的变量名和默认捕获模式来指定不同的捕获方式。parameter list
是参数列表,用于表示 Lambda表达式的参数,可以为空,表示没有参数,也可以和普通函数一样指定参数的类型和名称,还可以在 c++14 中使用auto
关键字来实现泛型参数。return type
是返回值类型,用于指定 Lambda表达式的返回值类型,可以省略,表示由编译器根据函数体推导,也可以使用 -> 符号显式指定,还可以在 c++14 中使用auto
关键字来实现泛型返回值。function body
是函数体,用于表示 Lambda表达式的具体逻辑,可以是一条语句,也可以是多条语句,还可以在 c++14 中使用constexpr
来实现编译期计算。
Lambda表达式的捕获方式
- 值捕获(capture by value):在捕获列表中使用变量名,表示将该变量的值拷贝到 Lambda 表达式中,作为一个数据成员。值捕获的变量在 Lambda 表达式定义时就已经确定,不会随着外部变量的变化而变化。值捕获的变量默认不能在 Lambda 表达式中修改,除非使用
mutable
关键字。例如:
int x = 10;
auto f = [x] (int y) -> int { return x + y; }; // 值捕获 x
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 15,不受外部 x 的影响
- 引用捕获(capture by reference):在捕获列表中使用 & 加变量名,表示将该变量的引用传递到 Lambda 表达式中,作为一个数据成员。引用捕获的变量在 Lambda 表达式调用时才确定,会随着外部变量的变化而变化。引用捕获的变量可以在 Lambda 表达式中修改,但要注意生命周期的问题,避免悬空引用的出现。例如:
int x = 10;
auto f = [&x] (int y) -> int { return x + y; }; // 引用捕获 x
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 25,受外部 x 的影响
- 隐式捕获(implicit capture):在捕获列表中使用
=
或&
,表示按值或按引用捕获 Lambda 表达式中使用的所有外部变量。这种方式可以简化捕获列表的书写,避免过长或遗漏。隐式捕获可以和显式捕获混合使用,但不能和同类型的显式捕获一起使用。例如:
int x = 10;
int y = 20;
auto f = [=, &y] (int z) -> int { return x + y + z; }; // 隐式按值捕获 x,显式按引用捕获 y
x = 30; // 修改外部的 x
y = 40; // 修改外部的 y
cout << f(5) << endl; // 输出 55,不受外部 x 的影响,受外部 y 的影响
- 初始化捕获(init capture):C++14 引入的一种新的捕获方式,它允许在捕获列表中使用初始化表达式,从而在捕获列表中创建并初始化一个新的变量,而不是捕获一个已存在的变量。这种方式可以使用
auto
关键字来推导类型,也可以显式指定类型。这种方式可以用来捕获只移动的变量,或者捕获this
指针的值。例如:
int x = 10;
auto f = [z = x + 5] (int y) -> int { return z + y; }; // 初始化捕获 z,相当于值捕获 x + 5
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 20,不受外部 x 的影响
使用 Lambda表达式作为函数参数
我们可以使用 Lambda表达式作为函数的参数,这样可以方便地定义和传递一些简单的函数对象,例如自定义排序规则、自定义比较函数等。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 定义一个结构体
struct Item
{
Item(int aa, int bb) : a(aa), b(bb) {}
int a;
int b;
};
int main()
{
vector<Item> vec;
vec.push_back(Item(1, 19));
vec.push_back(Item(10, 3));
vec.push_back(Item(3, 7));
vec.push_back(Item(8, 12));
vec.push_back(Item(2, 1));
// 使用 Lambda表达式,根据 Item 中的成员 a 升序排序
sort(vec.begin(), vec.end(), [] (const Item& v1, const Item& v2) { return v1.a < v2.a; });
// 使用 Lambda表达式,打印 vec 中的 Item 成员
for_each(vec.begin(), vec.end(), [] (const Item& item) { cout << item.a << " " << item.b << endl; });
return 0;
}
// https://leetcode.cn/problems/largest-number/
// 排序字符串,让int类型的数字按字典序从大到小排序
class Solution {
public:
string largestNumber(vector<int>& nums) {
sort(nums.begin(), nums.end(), [](const int &x, const int &y){
return to_string(x) + to_string(y) > to_string(y) + to_string(x);
});
string ans;
for(auto &i : nums) ans += to_string(i);
int i = 0;
int size = ans.length();
for(; i < size; i ++ )
{
if(ans[i] != '0') break;
}
return (i == size) ? "0" : ans.substr(i, size - i);
}
};
使用 Lambda表达式作为函数返回值
我们可以使用 Lambda表达式作为函数的返回值,这样可以方便地定义和返回一些简单的函数对象,例如工厂函数、闭包函数等。
#include <iostream>
using namespace std;
// 定义一个函数,返回一个 Lambda表达式,实现两个数的加法
auto make_adder(int x)
{
return [x] (int y) -> int { return x + y; };
}
int main()
{
// 调用函数,得到一个 Lambda表达式
auto add5 = make_adder(5);
// 调用 Lambda表达式
cout << add5(10) << endl; // 输出 15
return 0;
}
Lambda表达式与普通函数和普通类的关系
Lambda表达式虽然是一种语法糖,但它本质上也是一种函数对象,也就是重载了 operator() 的类的对象。每一个 Lambda表达式都对应一个唯一的匿名类,这个类的名称由编译器自动生成,因此我们无法直接获取或使用。Lambda表达式的捕获列表实际上是匿名类的数据成员,Lambda表达式的参数列表和返回值类型实际上是匿名类的 operator() 的参数列表和返回值类型,Lambda表达式的函数体实际上是匿名类的 operator() 的函数体。例如,下面的 Lambda表达式:
int x = 10;
auto f = [x] (int y) -> int { return x + y; };
相当于定义了一个匿名类,类似于:
int x = 10;
class __lambda_1
{
public:
__lambda_1(int x) : __x(x) {} // 构造函数,用于初始化捕获的变量
int operator() (int y) const // 重载的 operator(),用于调用 Lambda表达式
{
return __x + y; // 函数体,与 Lambda表达式的函数体相同
}
private:
int __x; // 数据成员,用于存储捕获的变量
};
auto f = __lambda_1(x); // 创建一个匿名类的对象,相当于 Lambda表达式
由于 Lambda表达式是一种函数对象,因此它可以赋值给一个合适的函数指针或函数引用,也可以作为模板参数传递给一个泛型函数或类。例如:
#include <iostream>
using namespace std;
// 定义一个函数指针类型
typedef int (*func_ptr) (int, int);
// 定义一个函数,接受一个函数指针作为参数
void apply(func_ptr f, int a, int b)
{
cout << f(a, b) << endl;
}
int main()
{
// 定义一个 Lambda表达式,计算两个数的乘积
auto mul = [] (int x, int y) -> int { return x * y; };
// 将 Lambda表达式赋值给一个函数指针
func_ptr fp = mul;
// 调用函数,传递函数指针
apply(fp, 3, 4); // 输出 12
return 0;
}
reverse()
翻转数组
//头文件
#include <algorithm>
//使用方法
reverse(a, a+n);//n为数组中的元素个数
#include <iostream>
#include <algorithm>
using namespace std;
void MyShow(int a[], int n)
{
for(int i = 0; i < n; i++)
cout << a[i] << ' ';
cout << endl;
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
//1 显示未翻转的数组内容
MyShow(a, 5);
//2 翻转数组然后再显示
reverse(a, a+5);
MyShow(a, 5);
return 0;
}
// 1 2 3 4 5
// 5 4 3 2 1
翻转字符串
//用法为
reverse(str.begin(), str.end());
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string str = "abcdefg";
//1 显示未翻转的字符串
cout << str << endl;
//2 翻转数组,然后显示
reverse(str.begin(), str.end());
cout << str << endl;
return 0;
}
// abcdefg
// gfedcba
翻转向量
//用法
reverse(vec.begin(), vec.end());
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void MyShow(vector<int> num)
{
for(int i = 0; i < num.size(); i++)
cout << num[i] << ' ';
cout << endl;
}
int main()
{
vector<int> vec = {1, 2, 3, 4, 5};
//1 显示未翻转的向量
MyShow(vec);
//2 翻转数组然后再显示
reverse(vec.begin(), vec.end());
MyShow(vec);
return 0;
}
// 1 2 3 4 5
// 5 4 3 2 1
sort等常用方法和技巧的更多相关文章
- ios基础篇(六)——UITextView的常用方法及技巧
上篇说到了UITextField,我们先来说说UITextView和UITextField的不同: UITextView支持多行输入; UITextFiled只支持单行; UITextView没有pl ...
- jQuery操作表格(table)的常用方法、技巧汇总
摘录自:http://www.jb51.net/article/48943.htm 虽然现在DIV+CSS进行页的布局大行其道,但是很多地方使用table还是有很多优势,用table展示数据是比较方便 ...
- javascript常用方法和技巧
浏览器变编辑器 data:text/html, <style type=;right:;bottom:;left:;}</style><div id="e" ...
- js常用方法和技巧
随着AJAX的流行,js又得到了很多人的重视,js最大的优势就是它能够对html上的所有元素进行操作,包括创建标签元素,更改元素属性等,这样就使得我们能够利用js来实现很多的动态效果,来提供给用户更强 ...
- jquery 操作表格 jQuery操作表格(table)的常用方法、技巧汇总
以下列出13个jQuery操作table常用到的功能: 1.鼠标移动行变色 $('#table1 tr').hover(function(){ $(this).children('td').addCl ...
- Thinkphp常用的方法和技巧(转)
2012年09月26日 Thinkphp 里一些常用方法和技巧的整理,包括常用的快捷键以及在程序开发时用到的一些实用方法,关于快捷键用得不是很熟练,总之,掌握这些方法和技巧,对于我们开发 thinkp ...
- 怎样用ZBrush对模型进行渲染
关于如何使用ZBrush®3D图形绘制软件雕刻僵尸模型,Fisker老师用了6个章节共41课时,从人体躯干和骨骼雕刻,到衣服.鞋子制作,再到顶点着色,向大家一一展示了雕刻过程,其中分享了很多ZBrus ...
- C/C++编程语言学习资料尽收眼底 电子书+视频教程
Visual C++(VC/MFC)学习电子书及开发工具下载请看这里 史无前例的网络最全最强C/C++资料索引: C/C++编程语言学习资料尽收眼底 电子书+视频教程 VC++/MFC(VC6)开发技 ...
- 2015老男孩Python培训第八期视频教程
2015老男孩Python培训第八期视频教程,希望您通过本教程的学习,能学会常用方法和技巧.教程从基础知识开始讲解一直到后期的案例实战,完全零基础学习,从初学者的角度探讨分析问题,循序渐进由易到难,确 ...
- stl——vector详解
stl——vector详解 stl——vector是应用最广泛的一种容器,类似于array,都将数据存储于连续空间中,支持随机访问.相对于array,vector对空间应用十分方便.高效,迭代器使ve ...
随机推荐
- docker部署ceph集群
1. 创建Ceph专用网络 sudo docker network create --driver bridge --subnet 172.20.0.0/16 ceph-network 2. 拉取搭建 ...
- 一次Java后端服务间歇性响应慢的问题排查记录
分享一个之前在公司内其它团队找到帮忙排查的一个后端服务连接超时问题,问题的表现是服务部署到线上后出现间歇性请求响应非常慢(大于10s),但是后端业务分析业务日志时却没有发现慢请求,另外由于服务容器li ...
- MySQL-删除数据和count(*)原理
delete删除数据原理 在InndoDB存储引擎中,delete删除操作是把需要删除的数据或者页标记为已删除,后面如果有需要,直接复用即可.这些被标记为已经删除的数据,看起来就像空洞一样.所以看起来 ...
- BigDecimal类--java进阶day05
1.BigDecimal出现的原因 2.BigDecimal的创建 不推荐第一种形式,会有误差 第二种方式创建对象 第三种方式调用方法 2.BigDecimal常用方法 除法的特殊事项 如果有除不尽的 ...
- Linux C线程读写锁深度解读 | 从原理到实战(附实测数据)
Linux C线程读写锁深度解读 | 从原理到实战(附实测数据) 读写锁练习:主线程不断写数据,另外两个线程不断读,通过读写锁保证数据读取有效性. 代码实现如下: #include <stdio ...
- 基于Redission实现分布式调度任务选主
在Spring Cloud微服务架构中,基于Redisson实现分布式调度任务的选主和心跳监听,可以通过以下完整方案实现.该方案结合了Redisson的分布式锁和发布/订阅功能,确保高可靠性和实时性: ...
- eolinker脚本代码[Javascript]:脚本(函数)之间传参案例
场景描述: 有等级卡A.B,依次通过查询A.B,检查A.B下是否会会员,如果有,调整会员身份,没有着执行下一个循环 全部脚本:脚本中红色标注的user即脚本之间的传参 //以下代码为示例代码(支持原生 ...
- Nacos简介—4.Nacos架构和原理
Nacos简介-4.Nacos架构和原理 大纲 1.Nacos的定位和优势 2.Nacos的整体架构 3.Nacos的配置模型 4.Nacos内核设计之一致性协议 5.Nacos内核设计之自研Dist ...
- 74.8K star!这个开源图标库让界面设计效率提升10倍!
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 Font Awesome 是全球最受欢迎的图标库和工具包,提供超过2000个免费图标和700 ...
- 【笔记】Python3|爬虫处理网页数据异步加载问题(结合Selenium完成)
文章目录 问题描述 1. 结合Selenium.Edge解析该网站搜索页面的数据 2. 结合lxml解析网页数据 3. 附加:不是异步加载的网页,结合requests直接请求数据 问题描述 一些网站会 ...