与普通函数区别:1.多了个template<class T>;2.某些确定类型变不确定类型T
一:引子:
#include<iostream>
using namespace std;
template<typename T>
T Max(T a, T b)
{
return a > b ? a : b;
}
int main()
{
int x, y;
double a, b;
cin >> x >> y >> a >> b;
cout << Max(a, b) << "\n" << Max(x, y);
}
结果:
回文数判断
1.回文数
1.回文数判断模板
模板:
#include <iostream>
#include <algorithm>
using namespace std;
template <typename T, size_t N>//size_t很多时候等价于int,但更安全,不会溢出
bool is_symmetric(T(&a)[N]) {//既可以接收数组,也可以接收字符串且传递了长度
size_t i=0, j= N - 1;
while (i <= j) {
if (a[i] != a[j])
return false;
++i, --j;
}
return true;
}
测试:
int main() {
int a[]{ 1, 2, 3, 4, 5, 4, 3, 2, 1 };
cout << is_symmetric(a) << endl; //1
cout << is_symmetric("aaaaaaaaaaaaaaaaa") << endl; //0
}
快速排序(双指针)
—分治思想
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n;
int q[N];
//****************************************
void quick_sort(int q[],int l,int r)
{
if(l>=r)return;
//确定分界点
int x=q[l],i=l-1,j=r+1;
while(i<j)
{
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j)swap(q[i],q[j]);
}
//递归处理左右两边
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
//********************************************
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i=0;i<n;i++)printf("%d ",q[i]);
return 0;
}
拓展1: 快速选择算法
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int q[N];
int n, k;
int quick_sort(int l, int r, int k)
{
if (l == r)return q[l];
int i = l - 1, j = r + 1, x = q[l];
while (i < j)
{
while (q[++i] < x);
while (q[--j] > x);
if (i < j)swap(q[i], q[j]);
}
int s1 = j - l + 1;//左半边长度
//确定在哪边之后对该部分递归快排即可,另一边不用排了(剪枝!!!)
if (k <= s1)return quick_sort(l, j, k);
else return quick_sort(j + 1, r, k - s1);
}
int main()
{
cin >> n >> k;
for (int i = 0; i < n; i++)cin >> q[i];
cout<<quick_sort(0, n - 1, k);
return 0;
}
归并排序(合二为一)
—分治思想
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n;
int q[N],temp[N];
//******************************************************************
void merge_sort(int q[],int l,int r)
{
if(l>=r)return;
//二分
int mid=l+r>>1;
//递归
merge_sort(q,l,mid);merge_sort(q,mid+1,r);
//归并
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(q[i]<=q[j])temp[k++]=q[i++];
else temp[k++]=q[j++];
}
while(i<=mid)temp[k++]=q[i++];
while(j<=r)temp[k++]=q[j++];
for(i=l,j=0;i<=r;i++,j++)q[i]=temp[j];
}
//******************************************************************
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&q[i]);
merge_sort(q,0,n-1);
for(int i=0;i<n;i++)printf("%d ",q[i]);
return 0;
}
拓展1:求逆序对数量
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int q[N];
int merge_sort(int l,int r)
{
if(l==r)return 0;
int tmp[N];
int mid=l+r>>1;
LL res=merge_sort(l,mid)+merge_sort(mid+1,r);
//归并过程
int i=l,j=mid+1,k=0;
while(i<=mid&&j<=r){
if(q[i]<=q[j])tmp[k++]=q[i++];
else
{
tmp[k++]=q[j++];
res+=mid-i+1;//在排序的过程同时求逆序对数量
}
}
//扫尾
while(i<=mid)tmp[k++]=q[i++];
while(j<=r)tmp[k++]=q[j++];
//物归原主
for(int i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];
return res;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)cin>>q[i];
cout<<merge_sort(0,n-1);
return 0;
}
5
5 4 1 2 3--------------------------------
Process exited after 30.19 seconds with return value 3221225725
请按任意键继续. . .
二分(整数)
高精度
加法
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N=1e6+10;
//******************************************************
vector<int> add(vector<int> &A,vector<int> &B)
{
//C=A+B
vector<int>C;
int t=0;//进位
for(int i=0;i<A.size()||i<B.size();i++)
{
if(i<A.size())t+=A[i];
if(i<B.size())t+=B[i];
int c=t%10;
C.push_back(c);
t/=10;
}
//最高位
if(t)C.push_back(1);
return C;
}
//******************************************************
int main()
{
string a,b;
vector<int>A,B;
cin>>a>>b;//a="123456"
for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');//A=[6,5,4,3,2,1]
for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
auto C=add(A,B);
for(int i=C.size()-1;i>=0;i--)cout<<C[i];
return 0;
}
23
39
62
--------------------------------
Process exited after 7.258 seconds with return value 0
请按任意键继续. . .
减法
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
//******************************************************
bool cmp(vector<int>& A, vector<int>& B)
{
if (A.size() != B.size())return A.size() > B.size();
for (int i = A.size() - 1; i >= 0; i--)
if (A[i] != B[i])return A[i] > B[i];
return true;
}
//******************************************************
vector<int> sub(vector<int>& A, vector<int>& B)
{
//C=A-B
vector<int>C;
int t = 0;//借位
for (int i = 0; i < A.size(); i++)
{
if (i < B.size())
{
if (A[i] - B[i] - t < 0)
{
C.push_back(10 + A[i] - B[i] - t);
t = 1;
}
else
{
C.push_back(A[i] - B[i] - t);
t = 0;
}
}
else
{
C.push_back(A[i] - t);
t = 0;
}
}
while (C.size()>1&&C[C.size() - 1] == 0)C.pop_back();
return C;
}
//******************************************************
int main()
{
string a, b;
vector<int>A, B;
cin >> a >> b;//a="123456"
//倒着存储
for (int i = a.size() - 1; i >= 0; i--)A.push_back(a[i] - '0');//A=[6,5,4,3,2,1]
for (int i = b.size() - 1; i >= 0; i--)B.push_back(b[i] - '0');
//确保是大减小
if (cmp(A, B))
{
auto C = sub(A, B);
for (int i = C.size() - 1; i >= 0; i--)cout << C[i];//易漏,两条件
}
else
{
auto C = sub(B, A);
cout << '-';
for (int i = C.size() - 1; i >= 0; i--)cout << C[i];
}
return 0;
}
22
33
-11
--------------------------------
Process exited after 5.188 seconds with return value 0
请按任意键继续. . .
乘法
//一长一短相乘(高精度*低精度)
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
//******************************************************
vector<int> mul(vector<int>& A, int b)
{
//C=A*b
vector<int>C;
int t = 0;//进位
for (int i = 0; i < A.size(); i++)
{
C.push_back((A[i] * b + t) % 10);
t = (A[i] * b + t) / 10;
}
//最后进位
while (t)
{
C.push_back(t % 10);
t /= 10;
}
return C;
}
//******************************************************
int main()
{
string a;
int b;
vector<int>A;
cin >> a >> b;//a="123456"
//倒着存储
for (int i = a.size() - 1; i >= 0; i--)A.push_back(a[i] - '0');//A=[6,5,4,3,2,1]
auto C = mul(A, b);
for (int i = C.size() - 1; i >= 0; i--)cout << C[i];
return 0;
}
1111
9999
11108889
D:\A.codefield\VS\Project2\x64\Debug\Project2.exe (进程 5556)已退出,代码为 0。
按任意键关闭此窗口. . .
除法
//一长一短相除(高精度/低精度)
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
//******************************************************
vector<int> div(vector<int>& A, int b, int& r)
{
//C=A/b
vector<int>C;
r = 0;
//除法从高位开始算
for (int i = A.size() - 1; i >= 0; i--)
{
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
//倒着排
reverse(C.begin(), C.end());
//防止出现123/123=001
while (C.size() > 1 && C.back() == 0)C.pop_back();
return C;
}
//******************************************************
int main()
{
string a;//被除数
int b;//除数
int r = 0;//余数
vector<int>A;
cin >> a >> b;//a="123456"
/*本应该正着存储(不存在进位或者借位问题,不需要倒着存储)
但是为了统一格式(加减乘都是倒着存储),这里也导致存储*/
for (int i = a.size()-1; i >= 0; i--)A.push_back(a[i] - '0');//A=[6,5,4,3,2,1]
auto C = div(A, b, r);
cout << "商=";
for (int i = C.size() - 1; i >= 0; i--)cout << C[i];
cout << endl << "余数=";
cout << r;
return 0;
}
123
11
商=11
余数=2
--------------------------------
Process exited after 3.994 seconds with return value 0
请按任意键继续. . .
双指针
eg1 双指针维护不重复队列
//双指针维护不重复队列
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int a[N],s[N];//a存储输入数据,s模拟包含不重复元素的队列(s[i]表示栈内i的数量)
int main()
{
int n,res=0;
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
//枚举窗口右端点j
for(int j=0,i=0;j<n;j++){
s[a[j]]++;//a[j]进栈
while(s[a[j]]>1)//如果a[j]进队列后队列内元素重复那只可能是新加入的a[j]重复
{
i++;
s[a[i]]--;//弹出队列
}
res=max(res,j-i+1);
}
cout<<res;
return 0;
}
离散化
eg1
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 300010;//n+2m
typedef pair<int,int> PII;
int n, m,a[N],s[N];
vector<int>alls;
vector<PII>add, query;
//找到x离散后的位置
//********************************************
int find(int x)
{
int l = 1, r = alls.size();
while (l < r) {
int mid = l + r >> 1;
if (alls[mid] >= x)r = mid;
else l = mid + 1;
}
return r;
}
//********************************************
int main()
{
cin >> n >> m;
//输入插入
for (int i = 0; i < n; i++) {
int x, c;
scanf("%d%d", &x, &c);
add.push_back({ x,c });
alls.push_back(x);
}
//输入查询
for (int i = 0; i < m; i++) {
int l, r;
cin >> l >> r;
alls.push_back(l); alls.push_back(r);
query.push_back({ l,r });
}
//去重
//************************************************************
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
//************************************************************
//插入
for (auto item : add) {
int x = find(item.first);
a[x] += item.second;
}
//求前缀和
for (int i = 1; i <=alls.size(); i++)s[i] = s[i - 1] + a[i];
//查询
for (auto item : query) {
int l = find(item.first), r = find(item.second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
数据结构
单链表
// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点
int head, e[N], ne[N], idx;
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 在链表头插入一个数a
void insert(int a)
{
e[idx] = a, ne[idx] = head, head = idx ++ ;
}
// 将头结点删除,需要保证头结点存在
void remove()
{
head = ne[head];
}
eg1
#include<iostream>
using namespace std;
const int N = 100010;
int head, e[N], ne[N], idx;
int n;
void init()
{
head = -1;
idx = 0;//当前处理位置下标
}
void add_head(int x)
{
e[idx] = x, ne[idx] = head, head = idx++;
}
void add_k(int k, int x)
{
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx++;
}
void remove(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
init();
cin >> n;
while (n--) {
char ch;
int k, x;
cin >> ch;
if (ch == 'H') {
cin >> x;
add_head(x);
}
else if (ch == 'I') {
cin >> k >> x;
add_k(k - 1, x);//因为下标是从0开始算的,所以第k个插入的点的下标为k-1
}
else if (ch == 'D') {
cin >> k;
if (k == 0)head = ne[head];//特判头结点
else remove(k - 1);
}
}
for (int i = head; i != -1; i = ne[i]) {
cout << e[i] << " ";
}
return 0;
}
双链表
// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;
// 初始化
void init()
{
//0是左端点,1是右端点
r[0] = 1, l[1] = 0;
idx = 2;
}
// 在节点a的右边插入一个数x
void insert(int a, int x)
{
e[idx] = x;
l[idx] = a, r[idx] = r[a];
l[r[a]] = idx, r[a] = idx ++ ;
}
// 删除节点a
void remove(int a)
{
l[r[a]] = l[a];
r[l[a]] = r[a];
}
栈
// tt表示栈顶
int stk[N], tt = 0;
// 向栈顶插入一个数
stk[ ++ tt] = x;
// 从栈顶弹出一个数
tt -- ;
// 栈顶的值
stk[tt];
// 判断栈是否为空,如果 tt > 0,则表示不为空
if (tt > 0)
{
}
队列
单调栈
--------- >左边比当前小且最近的树
//数组模拟单调栈
#include<iostream>
using namespace std;
const int N = 100010;
int n;
int stk[N], tt;//tt表示栈顶下标,当tt等于0时表示栈为空
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
int x;
for (int i = 0; i < n; i++) {
cin >> x;
while (tt && stk[tt] >= x)tt--;//弹出
if (tt)cout << stk[tt] << " ";
else cout << "-1 ";
stk[++tt] = x;//入栈
}
return 0;
}
//STL
#include<iostream>
#include<stack>
using namespace std;
const int N = 100010;
int n;
stack<int>stk;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
int x;
for (int i = 0; i < n; i++) {
cin >> x;
while (stk.size() && stk.top() >= x)stk.pop();
if (stk.size())cout << stk.top() << " ";
else cout << "-1" << " ";
stk.push(x);
}
return 0;
}
单调队列
------滑动窗口的最大值最小值
//数组模拟单调队列
#include<iostream>
using namespace std;
const int N = 1e6+10;
int a[N],que[N];//a存储值大小,que存储值下标
int n,k;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++)cin>>a[i];
int hh=0,tt=-1;
//求窗口内最小
for(int i=0;i<n;i++){//枚举窗口右端
//判断对头是否已经滑出窗口
if(hh<=tt&&i-k+1>que[hh])hh++;//弹出
while(hh<=tt&&a[que[tt]]>=a[i])tt--;//使得队列单调递增,!!!从队尾开始踢
que[++tt]=i;//入队
if(i>=k-1)cout<<a[que[hh]]<<" ";
}
cout<<"\n";
//求敞口内最大(与求滑动窗口最小值几乎一样)
hh=0,tt=-1;//!!!!!!!!!!1不能漏!!!!
for(int i=0;i<n;i++){
if(hh<=tt&&i-k+1>que[hh])hh++;
while(hh<=tt&&a[que[tt]]<=a[i])tt--;//这里符合不同
que[++tt]=i;
if(i>=k-1)cout<<a[que[hh]]<<" ";
}
return 0;
}
//STL(有一个数据过不去不知道为啥)
#include<iostream>
#include<cstring>
#include<deque>
using namespace std;
const int N = 1e6 + 10;
int a[N];//a存储值大小
deque<int>q;//队列存储值而非下标
int n, k;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 0; i < n; i++)cin >> a[i];
//求窗口内最小值
for (int i = 0; i < n; i++) {
if (!q.empty() && i-k>=0&&q.front()==a[i-k])q.pop_front();//非空且窗口越界
while (!q.empty() && a[i] <= q.back())q.pop_back();//只需维护窗口最小即可
q.push_back(a[i]);
if (i - k + 1 >= 0)cout << q.front() << " ";//达到窗口大小才输出
}
cout << "\n";
q.clear();
//求窗口内最大值
for (int i = 0; i < n; i++) {
if (!q.empty() && i-k>=0&&q.front()==a[i-k])q.pop_front();//非空且窗口越界
while (!q.empty() && a[i] >= q.back())q.pop_back();//只需维护窗口最小即可
q.push_back(a[i]);
if (i - k + 1 >= 0)cout << q.front() << " ";//达到窗口大小才输出
}
cout<<"\n";
return 0;
}
KMP
eg1
//暴力
#include<iostream>
#include<cstring>
using namespace std;
const int N=1010;
int n,m;
string s,p;
int cnt;
int ne[N];
bool check(int st)
{
for(int i=0;i<m;i++){
if(p[i]!=s[st++])return false;
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin>>n>>s>>m>>p;
int st,i,j;
for(int st=0;st<n;st++){
if(check(st))cout<<st<<" ";
}
return 0;
}