基础数据结构笔记
知识点
链表与邻接表:树与图的存储
单链表

画个图就很好理解了
例题
826. 单链表acwing——826. 单链表_awcing 826-CSDN博客
实现一个单链表,链表初始为空,支持三种操作:
(1) 向链表头插入一个数;
(2) 删除第k个插入的数后面的数;
(3) 在第k个插入的数后插入一个数
现在要对该链表进行M次操作,进行完所有操作后,从头到尾输出整个链表。
注意:题目中第k个插入的数并不是指当前链表的第k个数。例如操作过程中一共插入了n个数,则按照插入的时间顺序,这n个数依次为:第1个插入的数,第2个插入的数,…第n个插入的数。
输入格式
第一行包含整数M,表示操作次数。
接下来M行,每行包含一个操作命令,操作命令可能为以下几种:
(1) “H x”,表示向链表头插入一个数x。
(2) “D k”,表示删除第k个输入的数后面的数(当k为0时,表示删除头结点)。
(3) “I k x”,表示在第k个输入的数后面插入一个数x(此操作中k均大于0)。
输出格式
共一行,将整个链表从头到尾输出。
数据范围
1≤M≤100000
所有操作保证合法。
输入样例:
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6
输出样例:
6 4 6 5
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int m, k, x;
char op;
//init初始化
int head=-1, e[N], ne[N], idx=1;
// head 表示头结点的下标
// e[i] 表示结点i的值
// ne[i] 表示结点i的next指针是多少
// idx 存储当前已经用到了哪个点
// 将x插入到头结点
void add_to_head(int x)
{
e[idx]=x, ne[idx]=head, head=idx, idx++;
}
// 将下标是k后面的点删掉
void erase(int k)
{
ne[k]=ne[ne[k]];
}
// 将x插入到下标为k的后面
void add(int k, int x)
{
e[idx]=x, ne[idx]=ne[k], ne[k]=idx, idx++;
}
int main()
{
scanf("%d", &m);
while (m--)
{
cin>>op;
if (op=='H')
{
scanf("%d", &x);
add_to_head(x);
}
else if (op=='D')
{
scanf("%d", &k);
if (k==0) head=ne[head];
else erase(k);
}
else
{
scanf("%d%d", &k, &x);
add(k, x);
}
}
for (int i=head; i!=-1; i=ne[i]) printf("%d ", e[i]);
return 0;
}
双链表

题目来源:AcWing 827. 双链表
一、题目描述
实现一个双链表,双链表初始为空,支持 5 55 种操作:
在最左侧插入一个数;
在最右侧插入一个数;
将第 k kk 个插入的数删除;
在第 k kk 个插入的数左侧插入一个数;
在第 k kk 个插入的数右侧插入一个数
现在要对该链表进行 M MM 次操作,进行完所有操作后,从左到右输出整个链表。
注意:题目中第 k kk 个插入的数并不是指当前链表的第 k kk 个数。例如操作过程中一共插入了 n nn 个数,则按照插入的时间顺序,这 n nn 个数依次为:第 1 11 个插入的数,第 2 22 个插入的数,…第 n nn 个插入的数。
输入格式
第一行包含整数 M MM,表示操作次数。
接下来 M MM 行,每行包含一个操作命令,操作命令可能为以下几种:
L x,表示在链表的最左端插入数 x xx。
R x,表示在链表的最右端插入数 x xx。
D k,表示将第 k kk 个插入的数删除。
IL k x,表示在第 k kk 个插入的数左侧插入一个数。
IR k x,表示在第 k kk 个插入的数右侧插入一个数。
输出格式
共一行,将整个链表从左到右输出。
数据范围
1 ≤ M ≤ 100000 1≤M≤1000001≤M≤100000
所有操作保证合法。
输入样例:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
1
2
3
4
5
6
7
8
9
10
11
输出样例:
8 7 7 3 2 9
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int m, k, x;
string op;
int e[N], l[N], r[N], idx=0;
void init()
{
l[1]=0, r[0]=1;
idx=2;
}
void add(int k, int x)
{
e[idx]=x, l[idx]=k, r[idx]=r[k];
l[r[k]]=idx, r[k]=idx, idx++;
}
void erase(int k)
{
r[l[k]]=r[k];
l[r[k]]=l[k];
}
int main()
{
init();
scanf("%d", &m);
while (m--)
{
cin>>op;
if (op=="L")
{
scanf("%d", &x);
add(0, x);
}
else if (op=="R")
{
scanf("%d", &x);
add(l[1], x);
}
else if (op=="D")
{
scanf("%d", &k);
erase(k+1);
}
else if (op=="IL")
{
scanf("%d%d", &k, &x);
add(l[k+1], x);
}
else if (op=="IR")
{
scanf("%d%d", &k, &x);
add(k+1, x);
}
}
for (int i=r[0]; i!=1; i=r[i]) printf("%d ", e[i]);
return 0;
}
栈与队列:单调队列、单调栈

模拟栈

模拟队列

单调栈
用于找一个数左边或者右边离它最近的且比它小的数
例如3,4,2,7,5
输出-1,3,-1,2,2


栈里的元素一定是单调上升的
举例,刚开始数组8,7,4,8
8入栈,7再入栈,但是发现,这个8,不仅比7大,而且比7要往左。没用了,就不用管8了
题目信息
题目描述
给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 -1。
输入格式
第一行包含整数 N,表示数列长度。
第二行包含 N 个整数,表示整数数列。
输出格式
共一行,包含 N 个整数,其中第i个数表示第i个数的左边第一个比它小的数,如果不存在则输出 -1。
数据范围
1 ≤ N ≤ 1 0 5 10^510
5
1 ≤ 数列中元素 ≤ 1 0 9 10^910
9
输入样例
5
3 4 2 7 5
输出样例
-1 3 -1 2 2
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n, x, stk[N], tt=0;
int main()
{
scanf("%d", &n);
for (int i=1; i<=n; i++)
{
scanf("%d", &x);
while (tt && stk[tt]>=x) tt--;
if (tt) printf("%d ", stk[tt]);
else printf("-1 ");
stk[++tt]=x;
}
return 0;
}
单调队列
一般用于滑动窗口

取窗口内最小值, 如果一个数比当前这个数要大,而且位置靠前,就没用了
整个队列在滑动的过程中,在任何时刻都要保证严格的单调递增,因此窗口中的最小值永远是队首。最大值同理
题目来源:AcWing 154. 滑动窗口
一、题目描述
给定一个大小为 n ≤ 1 0 6 n≤10^6n≤10
6
的数组。
有一个大小为 k kk 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k kk 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7],k kk 为 3 33。
窗口位置 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 n nn 和 k kk,分别代表数组长度和滑动窗口的长度。
第二行有 n nn 个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3
1 3 -1 -3 5 3 6 7
1
2
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n, k, a[N];
int hh=1, tt=0, q[N];
int main()
{
scanf("%d%d", &n, &k);
for (int i=1; i<=n; i++) scanf("%d", &a[i]);
for (int i=1; i<=n; i++)
{
while (hh<=tt && i-k+1>q[hh]) hh++;
while (hh<=tt && a[q[tt]]>=a[i]) tt--;
q[++tt]=i;
if (i>=k) printf("%d ", a[q[hh]]);
}
printf("\n");
hh=1, tt=0;
for (int i=1; i<=n; i++)
{
while (hh<=tt && i-k+1>q[hh]) hh++;
while (hh<=tt && a[q[tt]]<=a[i]) tt--;
q[++tt]=i;
if (i>=k) printf("%d ", a[q[hh]]);
}
return 0;
}
KMP
sz算法,搞那么复杂,我就直接铥了。
AcWing 831. KMP字符串_acwing的kmp字符串-CSDN博客
KMP算法详解-彻底清楚了(转载+部分原创) - sofu6 - 博客园 (cnblogs.com)
“这玩意像老鼠屎一样,万年难得一用,不用细节就忘,忘了又回来看,过半年又忘”
例题
P3375 【模板】KMP - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
string s, p;
int n=0, m=0, ne[N];
int main()
{
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
cin>>s>>p;
s=' '+s, p=' '+p;
n=s.size()-1, m=p.size()-1;
//求模式串的Next数组:
for (int i=2, j=0; i<=m; i++)
{
while (j && p[i]!=p[j+1]) j=ne[j];
if (p[i]==p[j+1]) j++;
ne[i]=j;
}
//匹配
for (int i=1, j=0; i<=n; i++)
{
while (j && s[i]!=p[j+1]) j=ne[j];
if (s[i]==p[j+1]) j++;
if (j==m) //匹配成功
{
printf("%d\n", i-m+1);
j=ne[j];
}
}
for (int i=1; i<=m; i++) printf("%d ", ne[i]);
return 0;
}
Trie
建树例子


一般存完一个字符串要打上一个标记,在字符串的末尾(也可以是全部单词,如果求前缀的话)
清除字典数组的时候,不能用memset,会超时,要for循环到idx即可
#include <bits/stdc++.h>
using namespace std;
const int N=3e6+5;
int t, n, m, son[N][70], cnt[N], idx=0;
char s[N];
int ctoi(char x)
{
if (x>='A' && x<='Z') return x-'A'+1;
else if (x>='a' && x<='z') return x-'a'+27;
else if (x>='0' && x<='9') return x-'0'+53;
}
void insert(char c[])
{
int p=0;
for (int i=1; c[i]; i++)
{
int u=ctoi(c[i]);
if (!son[p][u]) son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;
}
int query(char c[])
{
int p=0;
for (int i=1; c[i]; i++)
{
int u=ctoi(c[i]);
if (!son[p][u]) return 0;
p=son[p][u];
}
return cnt[p];
}
int main()
{
scanf("%d", &t);
while (t--)
{
for (int i=0; i<=idx; i++)
for (int j=0; j<=68; j++)
son[i][j]=0, cnt[i]=0;
idx=0;
scanf("%d%d", &n, &m);
for (int i=1; i<=n; i++)
{
scanf("%s", s+1);
insert(s);
}
for (int i=1; i<=m; i++)
{
scanf("%s", s+1);
printf("%d\n", query(s));
}
}
return 0;
}
P8306 【模板】字典树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
并查集

(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
堆
这里维护小根堆

第三点,因为下标为1的数是最小值,但是不好删除,所以删最后一个点,好删一点
第四点,要分情况讨论,是down还是up,因为如果满足一些条件是不会执行down,up的,所以两个都写一遍
down,当一个数变大了,要往下调整
up,当一个数变小了,要往上调整

时间复杂度 O(nlogn)
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n, m, h[N], se=0;
void down(int u) //logn的时间,因为跟层数有关
{
int t=u;
if (u*2<=se && h[u*2]<h[t]) t=u*2;
if (u*2+1<=se && h[u*2+1]<h[t]) t=u*2+1;
if (t!=u)
{
swap(h[u], h[t]);
down(t);
}
}
int main()
{
scanf("%d", &n);
for (int i=1; i<=n; i++) scanf("%d", &h[i]);
se=n;
for (int i=n/2; i>=1; i--) down(i); // 1.*************分析时间***************
while (n--)
{
printf("%d ", h[1]);
h[1]=h[se--];
down(1);
}
return 0;
}
分析一下,1处时间

已知最后一层结点数为n/2,倒数第二层为n/4,第三层为n/8,第四层为n/16
因为最后一层不用下降了,所以不用加
然后倒数第二层最多down操作下降1层, 第三层最多down操作下降2层,第四层最多down操作下降3层,累加一下
n/4*1+n/8*2+n/16*3
转换成 n(1/4+2/8+3/16) =》 n(1/22+1/23+1/24)
令S=1/22+1/23+1/24
则2S=1/2+1/22+1/23
2S-S=S=1/2-1/24
已知S<1,所以nS<n,约等于log(n)
为什么建堆要从2分之n开始down?
AcWing 838. 堆排序 分析i=n/2 - AcWing
up操作
void up(int u)
{
while (u/2 && h[u/2]>h[u])
{
swap(h[u/2], h[u]);
u/=2;
}
}
题目来源:AcWing 839. 模拟堆
一、题目描述
维护一个集合,初始时集合为空,支持如下几种操作:
I x,插入一个数 x xx;
PM,输出当前集合中的最小值;
DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
D k,删除第 k kk 个插入的数;
C k x,修改第 k kk 个插入的数,将其变为 x xx;
现在要进行 N NN 次操作,对于所有第 2 22 个操作,输出当前集合的最小值。
输入格式
第一行包含整数 N NN。
接下来 N NN 行,每行包含一个操作指令,操作指令为 I x,PM,DM,D k 或 C k x 中的一种。
输出格式
对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。
每个结果占一行。
数据范围
1 ≤ N ≤ 1 0 5 1≤N≤10^51≤N≤10
5
− 1 0 9 ≤ x ≤ 1 0 9 −10^9≤x≤10^9−10
9
≤x≤10
9
数据保证合法。
输入样例:
8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM
1
2
3
4
5
6
7
8
9
输出样例:
-10
6
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n, k, x, h[N], hp[N], ph[N], m=0, se=0;
string op;
void heap_swap(int a, int b)
{
swap(h[a], h[b]);
swap(ph[hp[a]], ph[hp[b]]);
swap(hp[a], hp[b]);
}
void down(int u)
{
int t=u;
if (u*2<=se && h[u*2]<h[t]) t=u*2;
if (u*2+1<=se && h[u*2+1]<h[t]) t=u*2+1;
if (t!=u)
{
heap_swap(t, u);
down(t);
}
}
void up(int u)
{
while (u/2 && h[u/2]>h[u])
{
heap_swap(u/2, u);
u/=2;
}
}
int main()
{
scanf("%d", &n);
while (n--)
{
cin>>op;
if (op=="I")
{
scanf("%d", &x);
se++, m++;
h[se]=x;
hp[se]=m, ph[m]=se;
up(se);
}
else if (op=="PM") printf("%d\n", h[1]);
else if (op=="DM")
{
heap_swap(1, se);
se--;
down(1);
}
else if (op=="D")
{
scanf("%d", &k);
k=ph[k];
heap_swap(k, se);
se--;
down(k), up(k);
}
else if (op=="C")
{
scanf("%d%d", &k, &x);
k=ph[k];
h[k]=x;
down(k), up(k);
}
}
return 0;
}
哈希AcWing 840. 模拟散列表(拉链法+开放寻址法) - AcWing

一般先找一个>N的最近质数,作为模数
例如题目范围 1<=N<=1e5
for (int i=1e5;; i++)
{
int flag=1;
for (int j=2; j*j<=i; j++)
if (i%j==0)
{
flag=0;
break;
}
if (flag)
{
cout<<i;
break;
}
}
题目来源:AcWing 840. 模拟散列表
一、题目描述
维护一个集合,支持如下几种操作:
I x,插入一个数 x xx;
Q x,询问数 x xx 是否在集合中出现过;
现在要进行 N NN 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N NN,表示操作数量。
接下来 N NN 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。
输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x xx 在集合中出现过,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1 ≤ N ≤ 1 0 5 1≤N≤10^51≤N≤10
5
− 1 0 9 ≤ x ≤ 1 0 9 −10^9≤x≤10^9−10
9
≤x≤10
9
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
1
2
3
4
5
6
输出样例:
Yes
No
拉链法:
#include <bits/stdc++.h>
using namespace std;
const int N=100003;
int n, x;
char op;
vector<int> a[N];
void insert(int x)
{
int k=(x%N+N)%N;
a[k].push_back(x);
}
bool find(int x)
{
int k=(x%N+N)%N;
for (int i=0; i<a[k].size(); i++)
if (a[k][i]==x) return true;
return false;
}
int main()
{
scanf("%d", &n);
while (n--)
{
cin>>op;
scanf("%d", &x);
if (op=='I') insert(x);
else if (op=='Q')
{
if (find(x)) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
开放寻址法
#include <bits/stdc++.h>
using namespace std;
const int N=200003, oo=1e9+5; //比数据范围大一点即可
int n, x, a[N];
char op;
int find(int x)
{
int k=(x%N+N)%N;
while (a[k]!=oo && a[k]!=x)
{
if (k==N) k=0;
k++;
}
return k;
}
int main()
{
for (int i=0; i<N; i++) a[i]=oo;
scanf("%d", &n);
while (n--)
{
cin>>op;
scanf("%d", &x);
int k=find(x);
if (op=='I') a[k]=x;
else if (op=='Q')
{
if (a[k]!=oo) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
字符串哈希

注意,1.单个字母不能映射成0,
2.人品够好,不存在冲突
一般P取131或13331,Q取2^64


AcWing 841. 字符串哈希 【公式助理解】 - AcWing
[AcWing]841. 字符串哈希(C++实现)-CSDN博客

输入:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=1e5+5, P=131;
int n, m, l1, r1, l2, r2;
ULL p[N], h[N];
char a[N];
ULL get(int L, int R)
{
return h[R]-h[L-1]*p[R-L+1];
}
int main()
{
scanf("%d%d%s", &n, &m, a+1);
p[0]=1;
for (int i=1; i<=n; i++)
{
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+a[i];
}
while (m--)
{
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if (get(l1, r1)==get(l2, r2)) printf("Yes\n");
else printf("No\n");
}
return 0;
}
STL
vector, 变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back()
push_back()/pop_back()
begin()/end()
[]
支持比较运算,按字典序
pair<int, int>
first, 第一个元素
second, 第二个元素
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
string,字符串
size()/length() 返回字符串长度
empty()
clear()
substr(起始下标,(子串长度)) 返回子串
c_str() 返回字符串所在字符数组的起始地址
queue, 队列
size()
empty()
push() 向队尾插入一个元素
front() 返回队头元素
back() 返回队尾元素
pop() 弹出队头元素
priority_queue, 优先队列,默认是大根堆
size()
empty()
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出堆顶元素
定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
stack, 栈
size()
empty()
push() 向栈顶插入一个元素
top() 返回栈顶元素
pop() 弹出栈顶元素
deque, 双端队列
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
size()
empty()
clear()
begin()/end()
++, -- 返回前驱和后继,时间复杂度 O(logn)
set/multiset
insert() 插入一个数
find() 查找一个数
count() 返回某一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
lower_bound()/upper_bound()
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find()
[] 注意multimap不支持此操作。 时间复杂度是 O(logn)
lower_bound()/upper_bound()
unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
和上面类似,增删改查的时间复杂度是 O(1)
不支持 lower_bound()/upper_bound(), 迭代器的++,--
bitset, 圧位
bitset<10000> s;
~, &, |, ^
>>, <<
==, !=
[]
count() 返回有多少个1
any() 判断是否至少有一个1
none() 判断是否全为0
set() 把所有位置成1
set(k, v) 将第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反
vector中,a有3个元素,为4,4,4,b有4个元素,为3,3,3,3
如果用比较的话,结果是 a>b 的
3中vector的输出方式


a数组加入1000次元素1,空间长度为1,,与 加入1次,空间长度为1000,元素值为1,后者会更快,因为系统分配空间与内存无关,与申请次数有关
vector的分配空间类似于倍增,例如刚开始开32的空间,发现不够,就开64,一直这样,开n次,就是logn的时间复杂度,每一次相当于O(1)的时间复杂度
pair中,a={1,2}和a=make_pair(1,2)是等价的

如果用pair排序,是可以直接排序的,

pair存储三种东西也可以的
常用:排序,把需要排序的东西放first里面,不需要的放到second里面
字符串用格式化输出为
printf("%s", a.c_str());

priority_queue
一开始默认大根堆,就是堆
想要变成小根堆方法:
1.插入元素改成负数,取出来的时候再变正即可
2.直接定义小根堆

deque速度较慢,注意!

multimap
/*
*
********************************************
* multimap多重映照容器的基础说明:
********************************************
*
* multimap多重映照容器:容器的数据结构采用红黑树进行管理
* multimap的所有元素都是pair:第一元素为键值(key),不能修改;第二元素为实值(value),可被修改
*
* multimap特性以及用法与map完全相同,唯一的差别在于:
* 允许重复键值的元素插入容器(使用了RB-Tree的insert_equal函数)
* 因此:
* 键值key与元素value的映照关系是多对多的关系
* 没有定义[]操作运算
*
* Sorted Associative Container Pair Associative Container Unique Associative Container
*
* 使用multimap必须使用宏语句#include <map>
*
**************************************************************************************
*
* 创建multimap对象:
* 1.multimap<char,int,greater<char> > a; //元素键值类型为char,映照数据类型为int,键值的比较函数对象为greater<char>
* 2.multimap(const key_compare& comp) //指定一个比较函数对象comp来创建map对象
* 3.multimap(const multisetr&); //multimap<int,char*> b(a); //此时使用默认的键值比较函数less<int>
* 4.multimap(first,last);
* 5.multimap(first,last,const key_compare& comp);
*
* //Example:
* pair<const int ,char> p1(1,'a');
* pair<const int ,char> p2(2,'b');
* pair<const int ,char> p3(3,'c');
* pair<const int ,char> p4(4,'d');
* pair<const int ,char> pairArray[]={p1,p2,p3,p4};
* multimap<const int,char> m4(pairArray,pairArray+5);
* multimap<const int,char> m3(m4);
* multimap<const int,char,greater<const int> > m5(pairArray,pairArray+5,greater<const int>());
*
**************************************************************************************
*
* 元素的插入
* //typedef pair<const key,T> value_type;
* pair<iterator,bool> insert(const value_type& v);
* iterator insert(iterator pos,const value_type& v);
* void insert(first,last);
*
**************************************************************************************
*
* 元素的删除
* void erase(iterator pos);
* size_type erase(const key_type& k); //删除等于键值k的元素
* void erase(first,last); //删除[first,last)区间的元素
* void clear();
*
**************************************************************************************
*
* 访问与搜索
*
* iterator begin();iterator end(); //企图通过迭代器改变元素是不被允许的
* reverse_iterator rbegin();reverse_iterator rend();
*
* iterator find(const key_type& k) const;
* pair<iterator,iterator> equal_range(const key_type& k) const;//返回的pair对象,
* //first为lower_bound(k);大于等于k的第一个元素位置
* //second为upper_bound();大于k的第一个元素位置
*
* 其它常用函数
* bool empty() const;
* size_type size() const;
* size_type count(const key_type& k) const; //返回键值等于k的元素个数
* void swap();
*
* iterator lower_bound();iterator upper_bound();pair<iterator,iterator> equal_range();//上界、下届、确定区间
*
*
*
********************************************
** cumirror ** tongjinooo@163.com ** **
********************************************
*
*/
#include <map>
#include <string>
#include <iostream>
// 基本操作与set类型,牢记map中所有元素都是pair
// 对于自定义类,初学者会觉得比较函数如何构造很麻烦,这个可以参照前面的书写示例
// 但若设置键值为int或char类型,无须构造比较函数
struct student{
char* name;
int age;
char* city;
char* phone;
};
int main()
{
using namespace std;
student s[]={
{"童进",23,"武汉","XXX"},
{"老大",23,"武汉","XXX"},
{"饺子",23,"武汉","XXX"},
{"王老虎",23,"武汉","XXX"},
{"周润发",23,"武汉","XXX"},
{"周星星",23,"武汉","XXX"}
};
pair<int,student> p1(4,s[0]);
pair<int,student> p2(2,s[1]);
pair<int,student> p3(3,s[2]);
pair<int,student> p4(4,s[3]); //键值key与p1相同
pair<int,student> p5(5,s[4]);
pair<int,student> p6(6,s[5]);
multimap<int,student> a;
a.insert(p1);
a.insert(p2);
a.insert(p3);
a.insert(p4);
a.insert(p5);
a.insert(p6);
typedef multimap<int,student>::iterator int_multimap;
pair<int_multimap,int_multimap> p = a.equal_range(4);
int_multimap i = a.find(4);
cout<<"班上key值为"<< i->first<<"的学生有:"<<a.count(4)<<"名,"<<" 他们是:"<<endl;
for(int_multimap k = p.first; k != p.second; k++)
{
cout<<k->second.name<<endl;
}
cout<<"删除重复键值的同学"<<endl;
a.erase(i);
cout<<"现在班上总人数为:"<<a.size()<<". 人员如下:"<<endl;
for(multimap<int,student>::iterator j=a.begin(); j != a.end(); j++)
{
cout<<"The name: "<<j->second.name<<" "<<"age: "<<j->second.age<<" "
<<"city: "<<j->second.city<<" "<<"phone: "<<j->second.phone<<endl;
}
return 0;
}

浙公网安备 33010602011771号