《C++初阶之STL》【模板参数 + 模板特化 + 分离编译】

《C++初阶之STL》【模板参数 + 模板特化 + 分离编译】

【模板参数 + 模板特化 + 分离编译】

在这里插入图片描述
往期《C++初阶》回顾:

/------------ 入门基础 ------------/
【C++的前世今生】
【命名空间 + 输入&输出 + 缺省参数 + 函数重载】
【普通引用 + 常量引用 + 内联函数 + nullptr】
/------------ 类和对象 ------------/
【类 + 类域 + 访问限定符 + 对象的大小 + this指针】
【类的六大默认成员函数】
【初始化列表 + 自定义类型转换 + static成员】
【友元 + 内部类 + 匿名对象】
【经典案例:日期类】
/------------ 内存管理 ------------/
【内存分布 + operator new/delete + 定位new】
/------------ STL ------------/
【泛型编程 + STL简介】
【auto关键字 + 范围for循环 + 迭代器】
【string类:详解 + 实现】
【vector容器:详解 + 实现】
【list容器:详解 + 实现】
【stack/queue/priority_queue容器适配器:详解 + 实现】

前言:

hi~ 小伙伴们大家好呀!(≧∇≦)ノ 后天就是立秋了,夏天即将结束,但炎热的天气还没过去,可能还要持续两周左右呢~(>_<)
随着夏天的结束,《C++ 初阶》也迎来了最后一篇博客:【模板参数 + 模板特化 + 分离编译】 ✨。(๑¯∀¯๑))~这时可能会有小伙伴吐槽:看博主之前的博客,全是 “C 语言、算法、数据结构、C++” 初阶的这类内容,啥时候才能学到进阶的知识呀,哈哈~( ̄▽ ̄)ゞ 让大家久等了,但是这次关于C++的故事还并没结束,下期《C++ 进阶》敬请期待哦!(ノ◕ヮ◕)ノ:・゚✧

------------模板参数------------

C++的模板参数有哪些?

模板(Template):是泛型编程的核心机制,允许在编写代码时使用参数化的类型,从而实现代码的复用。

模板的参数分为两大类类型参数非类型参数,此外还有 模板模板参数(较少见):博主可没有打错字哦~,名字真的就叫作模板模板参数啊!

一、类型参数

类型参数(Type Parameters):表示模板中使用的数据类型可以是 内置类型intdouble)或者是 自定义类型:类、结构体)

1. 通用类型参数

2. 默认类型参数

可以为类型参数指定默认类型,调用时若未指定则使用默认值

template<typenameT=int>// 默认类型为 intclassStack{// ...}; Stack<> s;// 使用默认类型 int Stack<double> d;// 显式指定类型为 double

示例调用:

swap<int>(3,5);// 显式指定类型为 int swap<double>(3.14,2.71);// 显式指定类型为 double// 也可隐式推导类型:swap(3, 5);(编译器自动推导为 int)

使用class或typename声明,两者含义相同(推荐用typename,更清晰)

template<classT>// 类型参数 Tvoidswap(T& a, T& b){ T temp = a; a = b; b = temp;}template<typenameU>// 等价于 class UclassVector{/* ... */};

二、非类型参数

非类型参数(Non-Type Parameters):表示模板中使用的常量值通常为整型枚举值指针/引用C++11 后支持 std::nullptr_tconstexpr 变量等

1. 基本语法

2. 限制条件

对于指针/引用类型的非类型参数,要求其指向的对象具有静态存储期(如:全局变量static变量

int global_var =0;template<int* ptr>voidfunc(){/* ... */} func<&global_var>();// 合法,指向全局变量

非类型参数必须是编译期可确定的常量,不能是变量或运行时计算的值

Array<int,10> arr;// 合法,10 是编译期常量int n =10;// Array<int, n> arr; // 非法,n 是变量,非编译期常量

代码案例:非类型参数的使用小案例
namespace mySpace {//任务:定义的一个“静态数组”的模板类,同时要使用“非类型参数”template<classT, size_t N =10>classarray{private: T _array[N];//1.存储数据的静态数组 注意:在编译期确定数组大小 size_t _size;//2.记录数组中有效元素数量的变量public://1.实现:“普通版本的下标运算符[]的重载函数” T&operator[](size_t index)//注意:支持对数组元素的读写操作{return _array[index];}//2.实现:“const版本的下标运算符[]的重载函数”const T&operator[](size_t index)const//注意:保证在只读场景下也能通过下标访问元素,返回的是 const 引用,确保元素不会被修改{return _array[index];}//3.实现:“获取数组中有效元素的数量的操作” size_t size()const{return _size;}//4.实现:“判断数组是否为空的操作”boolempty()const{return _size ==0;}};}

三、模板模板参数

模板模板参数(Template Template Parameters):是指模板本身作为参数,用于将另一个模板传递给当前模板。

:暂时先了解一下概念即可)

------------模板特化------------

1. 什么是模板特化?

模板特化(Template Specialization):是模板机制的一个重要特性,允许针对特定的模板参数类型,提供模板的定制化实现。它允许针对特定类型,定制模板的行为,解决通用模板在特殊场景下的 “水土不服” 问题。当模板在某些特定类型下需要不同的行为或更高效的实现时,特化可以让代码更灵活、更贴合需求。

2. 为什么要使用模板特化?

在介绍的模板特化的时候,我们说模板特化是多么的厉害,但是口说无凭,模板特化真的有那么好吗?

下面的我们就来看一看模板特化的重要性。
#include<iostream>usingnamespace std;//任务1:定义一个日期类classDate{public:/*--------------成员变量--------------*/int _year;int _month;int _day;/*--------------成员函数--------------*///1.实现:“默认构造函数”Date(int y,int m,int d):_year(y),_month(m),_day(d){}//2.实现:“<运算符重载函数”booloperator<(const Date& other)const//注意:用于比较两个日期的先后顺序{if(_year != other._year)return _year < other._year;// 优先比较年份if(_month != other._month)return _month < other._month;// 年份相同则比较月份return _day < other._day;// 年份和月份都相同则比较日期}};//任务2:定义比较函数模板template<classT>boolLess(T x, T y){return x < y;//注意:依赖 T 类型的 operator< 实现}intmain(){// ---------------- 基础类型比较(正确行为)----------------// 1. 实例化 Less<int>(int, int)// 调用内置的 int 类型的 < 运算符 cout <<Less(1,2)<< endl;// ---------------- 对象类型比较(正确行为)----------------// 2. 实例化 Less<Date>(Date, Date)// 调用 Date 类重载的 operator< Date d1(2022,7,7); Date d2(2022,7,8); cout <<Less(d1, d2)<< endl;// ---------------- 指针类型比较(潜在问题)----------------// 3. 实例化 Less<Date*>(Date*, Date*)// 调用指针类型的 < 运算符(比较内存地址) Date* p1 =&d1;// p1 指向 d1 的内存地址 Date* p2 =&d2;// p2 指向 d2 的内存地址 cout <<Less(p1, p2)<< endl;// 可能输出 0 或 1(取决于内存地址的随机分配)//注意:此处本意是比较对象内容,但实际比较的是指针地址return0;}
在这里插入图片描述
可以看到,Less 函数在大多数情况下都能正常比较,但在特殊场景中会得出错误结果。

在上述示例里:p1 指向的 d1 显然小于 p2 指向的 d2 对象然而 Less 内部并没有比较 p1p2 所指向对象的内容而是比较了 p1p2 指针的地址,这就无法达成预期,进而出现错误

(哈哈,虽然博主在自己的VS上演示的结果没有出错,但是并不代表它没有问题呦)

这时,就需要对模板进行特化处理。也就是在原模板的基础上,针对特定类型,进行专门化的实现。

3. 模板特化有哪些?

模板特化的分类:

C++ 的模板特化可分为:函数模板特化类模板特化 两大类。

一、函数模板特化

函数模板特化的步骤

1. 先定义基础函数模板要特化函数模板,得先有一个通用的基础函数模板,它为各类型提供默认的泛型逻辑。这个模板能处理 intdouble自定义类(若重载了 < 运算符 )等类型的比较,但遇到指针类型时会因比较地址而非内容出问题,这就需要特化。

2. 添加特化声明与实现特化标识:用 template<> 表明这是一个模板特化(空尖括号表示不再推导模板参数明确特化类型:在函数名后的尖括号里,写上要专门处理的特定类型(如:Date* 类型,就写 Less<Date*>保持形参匹配:特化函数的形参列表,必须和基础函数模板的形参类型严格一致,否则编译器可能报错或匹配异常

比如:我们想实现 “比较两个值大小,返回 bool 结果” 的功能,先写通用模板:

template<classT>boolLess(T left, T right){return left < right;}

前面我们提到,Less 函数在比较 p1p2 时,内部并未比较它们所指向对象的内容,而是直接比较了指针的地址,这与预期不符,会导致错误。

此时,需要通过模板特化来解决这一问题。

既然已经了解了模板特化的方法,接下来我们就按照上面的步骤对代码进行特化处理吧!
#include<iostream>usingnamespace std;//任务1:定义一个日期类classDate{public:/*--------------成员变量--------------*/int _year;int _month;int _day;/*--------------成员函数--------------*///1.实现:“默认构造函数”Date(int y,int m,int d):_year(y),_month(m),_day(d){}//2.实现:“<运算符重载函数”booloperator<(const Date& other)const//注意:用于比较两个日期的先后顺序{if(_year != other._year)return _year < other._year;// 优先比较年份if(_month != other._month)return _month < other._month;// 年份相同则比较月份return _day < other._day;// 年份和月份都相同则比较日期}};//任务2:定义比较函数模板Lesstemplate<classT>boolLess(T left, T right){return left < right;//注意:依赖 T 类型的 operator< 实现}//任务3:对Less函数模板进行特化template<>bool Less<Date*>(Date* left, Date* right){return*left <*right;}intmain(){// ---------------- 基础类型比较(正确行为)----------------// 1. 实例化 Less<int>(int, int)// 调用内置的 int 类型的 < 运算符 cout <<Less(1,2)<< endl;// ---------------- 对象类型比较(正确行为)----------------// 2. 实例化 Less<Date>(Date, Date)// 调用 Date 类重载的 operator< Date d1(2022,7,7); Date d2(2022,7,8); cout <<Less(d1, d2)<< endl;// ---------------- 指针类型比较(潜在问题)----------------// 3. 实例化 Less<Date*>(Date*, Date*)// 调用指针类型的 < 运算符(比较内存地址) Date* p1 =&d1;// p1 指向 d1 的内存地址 Date* p2 =&d2;// p2 指向 d2 的内存地址 cout <<Less(p1, p2)<< endl;//注意:调用特化之后的版本了,而不是走通用模板了return0;}
在这里插入图片描述

函数模板全特化

函数模板全特化:为函数模板的所有参数显式指定类型,完全覆盖通用逻辑。示例:通用函数模板用于比较两个值的大小,但对 const char*(C 风格字符串),默认会比较指针地址而非内容,所以需要特化:

语法:

template<>//函数模板特化 返回类型 模板函数名<特化类型>(参数列表){...}
#include<iostream>#include<string>usingnamespace std;/*--------------------- 通用模板(求最大值)---------------------*/template<typenameT> T max_val(T a, T b){return a > b ? a : b;}/*------------------ 针对const char*类型的全特化 ------------------*/template<>// 函数模板全特化constchar* max_val<constchar*>(constchar* a,constchar* b)//按字符串字典序比较{returnstrcmp(a, b)>0? a : b;// 使用 C 风格字符串比较//注意:strcmp 返回值:a > b 则 >0,a < b 则 <0,相等则 0}intmain(){/*----------------调用通用模板:比较int值----------------*/ cout <<"-----调用通用模板:比较int值-----"<< endl; cout <<max_val(10,20)<< endl;/*---------------------- 调用全特化版本:比较字符串的内容 ----------------------*/ cout <<"-----调用全特化版本:比较字符串的内容-----"<< endl;constchar* s1 ="apple";constchar* s2 ="banana"; cout <<max_val(s1, s2)<< endl;//注意:自动匹配特化版本return0;}
在这里插入图片描述

函数模板偏特化

注意:C++不直接支持 函数模板偏特化(语法会报错),但可通过 函数重载 模拟类似效果。示例:max_val 对指针类型,比较指针指向的值,而非指针地址,通过重载实现:指针类型的 “偏特化”
#include<iostream>usingnamespace std;// 通用函数模板:比较值template<classT> T max_val(T a, T b){return a > b ? a : b;}// 重载版本:针对指针类型(模拟偏特化)template<classT> T*max_val(T* a, T* b){return*a >*b ? a : b;}intmain(){int x =10, y =20;// 调用通用模板:比较 int 值 cout <<max_val(5,3)<< endl;// 调用重载的指针版本:比较 *x 和 *yint* result =max_val(&x,&y); cout <<*result << endl;return0;}
在这里插入图片描述
原理:重载的 max_val(T* a, T* b) 并非严格意义的 “偏特化”,但利用函数重载决议,优先匹配指针类型的调用,达到 “针对部分类型定制” 的效果。若直接写函数模板偏特化语法(如:template <class T> T max_val<T*>(T* a, T* b) { ... }),编译器会报错,因此实际开发常用重载替代。

注意:虽然这种方式严格一点说并不能算作是函数模板特化,但是其实现简单、可读性高且易于书写。对于参数类型复杂的函数模板,使用特化反而可能会变得繁琐且容易出错,因此,不建议对函数模板进行特化,而应优先考虑使用函数重载类模板特化来替代。

二、类模板特化

类模板特化:为类模板的特定参数,定制类的实现,又分为:全特化和偏特化

类模板全特化

类模板全特化:为类模板的所有参数显式指定类型,完全替换通用类的实现。示例:假设有通用类模板 MyContainer,为 T=int, N=10 全特化:

语法:

template<>//空模板参数列表,表示特化class 模板类名<特化类型>//类模板特化{...};
#include<iostream>usingnamespace std;// 通用类模板:存储 T 类型,容量为 Ntemplate<classT, size_t N =10>classMyContainer{public:MyContainer(){ cout <<"通用类模板构造"<< endl;}voidprint(){ cout <<"通用容器:存储类型 T,容量 "<< N << endl;}};// 类模板全特化:针对 T=int,N=10template<>classMyContainer<int,10>{public:MyContainer(){ cout <<"全特化类构造(int, 10)"<< endl;}voidprint(){ cout <<"特化容器:专门存储 int,容量 10(定制逻辑)"<< endl;}};intmain(){ cout <<"-----------调用通用类模板-----------"<< endl; MyContainer<double,5> c1; c1.print(); cout <<"-----------调用全特化类-----------"<< endl; MyContainer<int,10> c2; c2.print();return0;}
在这里插入图片描述
关键说明:全特化类的实现与通用类完全独立,可自定义构造函数、成员函数等。当实例化 MyContainer<int, 10> 时,编译器优先选择全特化版本。

类模板偏特化

类模板偏特化:对模板参数的部分类型特定条件进行特化,而非全部参数。

偏特化适用场景:参数数量特化:如模板有两个参数,特化其中一个。参数范围特化:如特化指针类型、引用类型、const 类型等。

第一种场景针对模板参数数量的偏特化
/*------------------------通用模板(两个参数)------------------------*/template<typenameT1,typenameT2>classMyClass{...};/*----------------------偏特化为第一个参数固定为int---------------------*/template<typenameT2>// 保留第二个参数 T2classMyClass<int, T2>{...};// 特化第一个参数为 int
第二种场景针对模板参数范围的偏特化
/*----------------------------通用模板----------------------------*/template<typenameT>classMyClass{public:voidprint(T value){ cout <<"通用类型: "<< value << endl;}};/*------------------------针对指针类型的偏特化-------------------------*/template<typenameT>// 仍保留一个模板参数 T,表示指针指向的类型classMyClass<T*>// 特化指针类型{public:voidprint(T* ptr){ cout <<"指针地址: "<< ptr << endl;}};/*---------------------- 针对const类型的偏特化-----------------------*/template<typenameT>classMyClass<const T>// 特化 const T 类型{// 处理 const 类型的逻辑};/*----------------------------- 调用 -------------------------------*/ MyClass<int> obj1;// 通用类型,int obj1.print(10);// 输出:通用类型: 10 MyClass<int*> obj2;// 特化指针类型,int*int x =20; obj2.print(&x);// 输出:指针地址: 0x7fff...
示例:指针类型的偏特化:让 MyContainer 对指针类型 T*,定制存储逻辑(如:打印指针地址而非值)
#include<iostream>usingnamespace std;// 通用类模板:存储 T 类型,容量为 Ntemplate<classT, size_t N =10>classMyContainer{public:MyContainer(){ cout <<"通用类模板构造"<< endl;}voidprint(){ cout <<"通用容器:存储类型 T,容量 "<< N << endl;}};// 类模板偏特化:针对 T 是指针类型(T*)template<classT, size_t N>classMyContainer<T*, N>{public:MyContainer(T* data):_data(data){ cout <<"偏特化类构造(指针类型)"<< endl;}voidprint(){ cout <<"存储指针:地址 = "<< _data << endl;}private: T* _data;};intmain(){ cout <<"-----------调用通用类模板-----------"<< endl; MyContainer<double,5> c1; c1.print(); cout <<"-----------调用指针类型的偏特化-----------"<< endl;int x =100; MyContainer<int*>c2(&x);// 调用偏特化类(T=int*, N=默认 10) c2.print();return0;}
在这里插入图片描述
说明:偏特化后,MyContainer<T*, N> 中的 T 仍为泛型(如:int),N 也保留默认值,但约束了 T 必须是指针类型。实例化 MyContainer<int*> 时,自动匹配偏特化版本。

回顾与总结:

Less 函数在比较 p1p2(指针)时,内部没有比较它们指向对象的实际内容,而是直接比较了指针的地址,这与我们想要比较对象内容的预期不符,会引发错误。这时,我们可以用模板特化解决问题,所以之前我们尝试过用 函数模板全特化 处理但是后来我们提到过:对于参数类型复杂的函数模板,特化过程容易变得繁琐、易错。所以,不建议直接对函数模板特化,更推荐优先用 函数重载类模板特化 替代。

前面我们已经演示了用函数重载解决指针比较问题,接下来就用类模板特化的方式,处理这个场景,让比较逻辑符合预期。
#include<vector>#include<algorithm>usingnamespace std;//任务1:定义一个日期类classDate{public:/*--------------成员变量--------------*/int _year;int _month;int _day;/*--------------成员函数--------------*///1.实现:“默认构造函数”Date(int y,int m,int d):_year(y),_month(m),_day(d){}//2.实现:“<运算符重载函数”booloperator<(const Date& other)const//注意:用于比较两个日期的先后顺序{if(_year != other._year)return _year < other._year;// 优先比较年份if(_month != other._month)return _month < other._month;// 年份相同则比较月份return _day < other._day;// 年份和月份都相同则比较日期}};//任务2:定义比较类模板Less(仿函数)template<classT>structLess{//1.重载()运算符,使该结构体可像函数一样调用booloperator()(const T& x,const T& y)const{return x < y;//注意:依赖 T 类型的 operator< 实现}};//任务3:比较类模板Less按照指针方式特化template<>structLess<Date*>{booloperator()(Date* x, Date* y)const{return*x <*y;}};intmain(){// 创建三个日期对象(d2 < d1 < d3) Date d1(2022,7,7); Date d2(2022,7,6); Date d3(2022,7,8);// ---------------- 存储对象的 vector 排序(正确行为)---------------- vector<Date> v1; v1.push_back(d1); v1.push_back(d2); v1.push_back(d3);// 使用 Less<Date> 作为比较器// 实例化 Less<Date>,调用 Date::operator< 比较对象内容sort(v1.begin(), v1.end(), Less<Date>());// ---------------- 存储指针的 vector 排序(潜在问题)---------------- vector<Date*> v2; v2.push_back(&d1); v2.push_back(&d2); v2.push_back(&d3);// 使用特化模板 Less<Date*> 作为比较器// 比较的是指针指向的对象内容(*x < *y),而非指针地址sort(v2.begin(), v2.end(), Less<Date*>());return0;}
错误情况:若未进行类模板Less的指针方式特化处理
在这里插入图片描述
正确情况:若进行了类模板Less的指针方式特化处理
在这里插入图片描述

------------分离编译------------

什么是分离编译?

分离编译:是一种软件开发中的编译策略,指将程序的不同部分(如:不同的源文件)分别编译为目标代码(.obj.o文件),最终再通过链接器将这些目标文件和依赖的库文件合并成可执行程序库文件的过程。

核心思想:

将大型程序拆解为多个独立编译单元(源文件),每个单元单独编译,减少重复编译的开销,提高开发效率


关键机制:声明与定义分离:头文件(.h)放声明,源文件(.cpp)放实现编译单元:每个.cpp文件及其包含的头文件构成独立编译单元符号决议:链接器负责解决跨文件的函数/变量引用
在这里插入图片描述

模板的分离编译要注意什么事情?

模板的分离编译是 C++ 中一个容易引发错误的复杂问题,主要源于模板实例化机制传统分离编译模型的不兼容

传统分离编译流程:编译器独立处理每个.cpp文件,生成对应的.obj文件链接器将所有.obj文件合并,解析未定义的符号(如:函数调用)

模板实例化机制:模板代码(如:template <class T> void func(T x))本身不是完整代码需要在使用时根据实参类型(如:int)实例化出具体代码(如:void func(int x)

矛盾点:若模板定义(如:func的实现)放在.cpp文件中,编译器编译该文件时无法得知未来会被哪些类型实例化,因此不会生成具体代码当其他文件(如:main.cpp)使用该模板时,编译器只能看到模板声明,无法找到对应实例化的定义,导致链接错误
常见错误示例:错误的分离编译结构
/*--------------------------func.h(声明模板)--------------------------*/template<classT>voidfunc(T x);/*--------------------------func.cpp(定义模板)--------------------------*/#include"func.h"template<classT>voidfunc(T x){/* ... */}/*--------------------------main.cpp(使用模板)--------------------------*/#include"func.h"intmain(){func(42);// 编译时找不到 func<int> 的定义,链接错误!return0;}

怎么解决模板分离编译时带来的问题?

解决模板分离编译问题的方法主要有两种:将模板定义放在头文件中使用显式实例化

最推荐第一种方法。毕竟,解决模板分离编译问题的核心就是:让编译器在实例化模板时能同时看到声明与定义。将声明和定义写在同一个头文件中,从根源上避免分离编译带来的符号解析问题,是最简单直接且兼容性最好的方案。

1. 将模板定义放在头文件中原理:让编译器在使用模板的编译单元(如:main.cpp)时同时看到声明和定义,直接实例化代码。优点:简单直接,无需额外操作。缺点:头文件包含实现细节,可能导致代码膨胀。修改实现需重新编译所有包含该头文件的源文件。

2. 使用显式实例化原理:在模板定义文件中显式指定需要实例化的类型,强制编译器生成对应代码。优点:保持分离编译结构,避免头文件包含实现。缺点:需预先知道所有会被使用的实例化类型,不灵活。新增类型需修改.cpp文件并重新编译。

示例

/*---------------------------func.h(声明模板)---------------------------*/template<classT>voidfunc(T x);/*---------------------------func.cpp(定义模板并显式实例化)---------------------------*/#include"func.h"//1.定义模板template<classT>voidfunc(T x){/* ... */}//2.显式实例化 int 类型templatevoid func<int>(int);/*---------------------------main.cpp(使用模板)---------------------------*/#include"func.h"intmain(){func(42);// 使用已显式实例化的 func<int>return0;}

示例

/*---------------------------func.h---------------------------*/template<classT>voidfunc(T x){/* 直接在头文件中定义 */}/*---------------------------main.cpp---------------------------*/#include"func.h"intmain(){func(42);// 编译器在此处实例化 func<int>return0;}
在这里插入图片描述

Read more

算法基础篇:(二十一)数据结构之单调栈:从原理到实战,玩转高效解题

算法基础篇:(二十一)数据结构之单调栈:从原理到实战,玩转高效解题

目录 前言 一、什么是单调栈?先打破 “栈” 的常规认知 1.1 单调栈的核心特性 1.2 如何实现一个单调栈? 实现单调递增栈 实现单调递减栈 1.3 核心操作解析:为什么要 “弹出元素”? 二、单调栈能解决什么问题?四大核心场景全覆盖 2.1 场景 1:找左侧最近的 “更大元素” 问题描述 解题思路 代码实现 测试用例验证 2.2 场景 2:找左侧最近的 “更小元素” 问题描述 解题思路 代码实现 测试用例验证 2.3 场景 3:找右侧最近的 “更大元素” 问题描述

By Ne0inhk
【数据结构初阶】排序算法(中)快速排序专题

【数据结构初阶】排序算法(中)快速排序专题

文章目录 * 1. 快排主框架 * 2. 快排的不同实现 * 2. 1 hoare版本 * 2. 2 挖坑法 * 2. 3 lomuto前后指针法 * 2. 4 快排的非递归版本 * 3. 快排优化 * 3. 1 快排性能的关键点分析: * 3. 1 三路划分 * 3. 2 introsort自省排序 1. 快排主框架 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。 其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。 简单地说,就是将数组分成左右两个部分,左部分都大于(或小于)中间的基准值,右部分都小于(或大于)中间的基准值,然后不断重复上述过程,直到数组完全有序。 //快排主框架voidQuickSort(int*

By Ne0inhk
动态规划 线性 DP 经典四题一遍吃透

动态规划 线性 DP 经典四题一遍吃透

文章目录 * 台阶问题 * 最大子段和 * 传球游戏 * 乌龟棋 线性dp 是动态规划问题中最基础、最常⻅的⼀类问题。它的特点是状态转移只依赖于前⼀个或前⼏个状态,状态之间的关系是线性的,通常可以⽤⼀维或者⼆维数组来存储状态。 我们在⼊⻔阶段解决的《下楼梯》以及《数字三⻆形》其实都是线性dp,⼀个是⼀维的,另⼀个是⼆ 维的。 台阶问题 题目描述 题目解析 本题就是上一节下楼梯的问题的加强版,总体思路不变,下面我们还是按照动规5板斧来分析一下这道题。 1、状态表示 dp[i]表示走到第i个台阶的所有方案数 2、状态转移方程 第i个台阶的方案数等于从i-1阶到i-k阶的所有方案数之和,因为本题数据比较大,用long long都无法保证数据不越界,所以题目规定方案数还需要模100003,第i个台阶的方案数等于从i-1阶到i-k阶的所有方案数之和再模上100003,所以但是注意是可能越界访问的,比如i为3,

By Ne0inhk
《算法闯关指南:动态规划算法--斐波拉契数列模型》--01.第N个泰波拉契数,02.三步问题

《算法闯关指南:动态规划算法--斐波拉契数列模型》--01.第N个泰波拉契数,02.三步问题

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 01.第N个泰波拉契数 * 解法(动态规划): * 算法流程: * C++算法代码: * 算法总结&&笔记展示: * 02.三步问题 * 解法(动态规划): * 算法思路: * C++算法代码: * 算法总结&&笔记展示: * 结尾: 前言: 聚焦算法题实战,系统讲解三大核心板块:优选算法:剖析动态规划、二分法等高效策略,学会寻找“最优解”。 递归与回溯:掌握问题分解与状态回退,攻克组合、排列等难题。 贪心算法:理解“

By Ne0inhk