提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
2024年7月8号到7月13号学习内容
一、数据结构
单链表的操作
//初始化
void init()
{
head = -1;
idx = 0;
}
//向表头插入元素,e[]-元素的值,ne[]元素的指针域,idx表示用到底几个下标
add_to_head(int x)
{
e[idx] = x;//将x赋值给第idx个节点
ne[idx] = head;//将idx的指针域指向head头指针指向的位置
head = idx;//将头指针指向idx节点
idx++;
}
//指定位置插入元素
add(int x)
{
e[idx] = x;//将x赋值
ne[idx] = ne[k]//将第idx的指针域指向第k个元素指向的位置,即第k+1的元素的位置
ne[k] = idx;//将第k个元素的指针域指向第idx个元素
idx++;
}
//删除元素,将第k个元素的指针域的指向改变即可
remove(int k)
{
ne[k] = ne[ne[k]];
}
双链表的操作
//r[]:元素的后继指针,l[]:元素的前驱指针
//初始化
void init()
{
r[0] = 1;//0的后继指针指向下标为1的元素
l[1] = 0;//1的前驱指针指向下标为0的元素,形成双向链表
idx = 2;//已经用了两个节点
}
//在下标为k的右边插入x
void add(int x,int k)
{
e[idx] = x;
r[idx] = r[k];//将idx的后继指针指向第k个元素的后继指针指向的位置
l[idx] = k;//将idx的前驱指针指向第k个元素的前驱指针指向的位置
l[r[k]] = idx;//将第k个元素的后继指针指向的元素的前驱指针指向idx;
r[k] = idx;//将第k个元素的后继指针指向idx;
idx++;
}
//删除第k个元素
void remove(int k)
{
l[r[k]] = l[k];
r[l[k]] = r[k];
}
数组模拟栈
int stk[N],tt = 0;//stk[]代表栈,tt表示栈顶指针,当栈内没有元素是tt=0,并且从第一个位置开始存
void push(int x)
{
stk[++tt] = x;//将栈顶指针向上移动,并赋值
}
void pop()
{
tt--;//并不是将栈顶元素删除而是将栈顶指针移动,当栈内新增元素时tt会自增从而将先前的元素覆盖
}
//取出栈顶元素stk[tt];
int empty()
{
return tt;
}
数组模拟队列
int q[N],hh= 0,tt = -1;//q[]代表队列,hh表示队头,tt表示队尾,当队列内没有元素是hh==tt,并且从第一个位置开始存
void push(int x)
{
q[++tt] = x;//将栈顶指针向上移动,并赋值
}
void pop()
{
hh++;//将队首指针向后移动即可
}
//取出栈顶元素q[tt];
int empty()
{
return hh==tt;//当队首指针等于队尾指针时,队列为空
}
单调栈
计算每一个数左边距离最近且比他小的数的位置
for(int i = 0;i<n;i++)
{
int x;
cin>>x;
while(tt&&stk[tt]>=x)
tt--;//出栈
if(tt)
cout<<stk[tt];
else
cout<<"-1";
KMP字符串匹配
//朴素做法:暴力
for(int i = 1;i<=n;i++)
{
int flag = 1;
for(int j = 1;j<=m;j++)
{
if(s[i] != p[j];
{
flag = 0;
break;
}
}
}
//求ne[]的过程,针对的是模式串,起始位置为1
//求的是第i个元素的ne[],即当模式串中的第i个元素与主串的某个位置不匹配时,应当回到模式串中的哪个位置重新开始比较
for(int i = 2,j = 0;i<=n;i++)
{
while(j&&p[i]!=p[j+1])//当发现不匹配时,就回到之前的位置
j = ne[j];
if(p[i] == p[j+1])
j++;
ne[i] = j;//当不匹配时ne[i] = ne[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)
{
cout<<i - m + 1);//打印主串中与模式串匹配的子串的第一个元素的下标
}
//ne数组是记录当s[i] != p[j+1]时,j应该回退多少
Tire树
快速的存储和查找字符串
#include<iostream>
using namespace std;
const int N = 100010;
int idx;
int son[N][26];
int cnt[N];
void insert(string a)
{
int p = 0;
for (int i = 0; a.size(); i++)
{
int u = a[i] - 'a';
if (!son[p][u])
son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
int query(string a)
{
int p = 0;
for (int i = 0; i < a.size(); i++)
{
int u = a[i] - 'a';
if (!son[p][u])
return 0;
p = son[p][u];
}
return cnt[p];
}
int main()
{
int n;
cin >> n;
while (n--)
{
char op;
cin >> op;
string x;
if (op == 'I')
{
cin >> x;
insert(x);
}
else
{
cin >> x;
cout << query(x) << endl;
}
}
return 0;
}
并查集(优化:路径压缩)
1、将两个集合合并
2、询问两个元素是否在一个集合内
原理:
每个集合用一棵树来表示,树根的编号就是整个集合的编号,每个节点存储他的父节点,p[x]表示x的父节点
①如何判断树根
if(p[x]==x)
②如何求x的集合编号
int find(int x) //查找x的祖宗节点
{
if(pre[x] == x) return x; //递归出口:x的上级为 x本身,即 x为根结点
return pre[x] = find(pre[x]);
}
堆(小根堆)
主要是堆如何手写(用一维数组存储堆)
若当前节点为x则左儿子的下标为2x,右儿子的下标为2x+1(满足完全二叉树)
主要的操作有:
①向集合中插入一个数
heap[++size] = x;//先将节点的值加入数组中
up(size);//再用up函数将元素向上移动,因为是存储在此时数组下的最后一个位置,所以向前寻找应该存放的位置
②求集合中的最小值
heap[1];//小根堆最小值在堆顶
③删除最小值
heap[1] = heap[size]//将堆顶用最后一个元素覆盖
size--;//数组长度减一
down(1);//将覆盖后的堆顶元素用down函数向下寻找合适的位置
④删除任意一个元素
hepa[k] = heap[size];//用最后一个元素将第k个元素覆盖
size--;//数组长度减一
down(k),up(k);//用down函数向下寻找第k个元素的位置,若向下找到合适的位置,up函数不会执行
⑤修改任意一个元素
heap[k] = x;
down(k),up(k);
小根堆:每个节点均小于本身的子节点
基本操作
void down(int u)
{
int t = u;
if(u*2<=size&&heap[u*2]<heap[t]) t = u*2;//判断是否有左儿子和是否比左儿子大,若大则更改下标,下同
if(u*2_1<=size&&heap[u*2+1]<h[t])t = u*2+1;
if(u!=t)//说明下标变了·
heap_swqp(h[u],h[t]);//交换两个元素;
down(t);//将第t个元素继续下移
}
void up(int k)
while(u/2&&h[u/2]>h[u])//判断是否存在父节点并且父节点是否比自己大
{
heap_swap(heap[u/2],heap[u]);//大的话就交换
u/=2;//下标向上移动
}
//ph[k]:第k个插入的数的下标
//hp[k]:堆中的某个点是第几个插入的点
void heap_swap(int a,int b)
{
swap(ph[hp[a]],ph[hp[b]]);//下标交换
swap(hp[a],hp[b]);//插入的次序交换
swap(heap[a],heap[b]);//值交换
}
哈希表
十的负九次方到十的九次方中的数映射到0到十的五次方(利用取模,用质数进行取模)
拉链法
void insert(int x)
{
int k = (x%N+N)%N;//让负数取模变为正数
e[idx] = x;
ne[idx] = h[k];//将第idx个元素的指针指向第k个元素的指针指向的位置
h[k] = idx++;//将第k个元素的指针指向idx;
//h[k]:表示的是第k个链表的下标
}
bool find(int x)
{
int k = (x%N+N)%N;//让负数取模变为正数
for(int i = h[k];i!=-1;i = ne[i])//找到元素x取模的值在h[]数组中的位置
{
if(e[i]==x)//若对应链表下存在x
return true;
}
return false;
}
开放寻址法
元素若存在返回下标,若不存在,返回应当插入的位置的下标
int null = 0x3f3f3f3f;
memset(h,0x3f,sizeof(h));//memset按字节初始化
int find(int x)
{
int k = (x%N+N)%N;
while(h[k]!= null&&h[k]!=x)//查询时:判断有人并且不是x,就往后找,如果是x就返回下标
//插入时:判断该位置上是否存储元素,若没有元素即可插入元素
{
k++;
if(k==N)
k==0;//已经找到最后一个了,就回到开头重新找
}
//找不到的话一定会因为h[[k]==null而停止,因为数组开的足够大
return k;
字符串哈希
核心:将字符串看成P进制的数,再转换成十进制,结果对Q取模,p取131或13331,去这两个值的冲突概率较低,mod用2^64,这样直接用unsigned long long 存储,溢出的结果就是取模的结果
typedef unsigned long long ULL;
UUL h[N],p[K]//h[k]存储字符串前k个字母的哈希值,p[k]存储p^kmod2^64
p[0] = 1;
for(int i = 1;i<=n;i ++)
{
h[i] = h[i-1]*p+str[i];
p[i] = p[i-1]*p;
}
//计算子串str[1~r]的哈希值
UUL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
C++ STL 简介
vector, 变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back()
push_back()/pop_back()
begin()/end()
支持比较运算,按字典序
vectot<int> a(10,3) 定义一个长度为10的数组并初始化为3
pair<int, int>
first, 第一个元素
second, 第二个元素
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
p = make_pair{10,"hrx"}或p = {10,"hrx"}
某个元素的某个属性需要排序可以放在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 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()
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位取反
总结
2024年7月17日完毕