一些基础数据结构

一些基础数据结构

Trie树

高效地存储查找字符串集合的数据结构

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int son[N][26],cnt[N],idx;//下标是0的点既是根节点又是空节点
char str[N];
void insert(char str[])
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'a';//将字母映射到下标上
        if(!son[p][u])son[p][u]=++idx;//如果当前节点的儿子没有这个字母,就创建一个新节点
        p=son[p][u];
    }
    cnt[p]++;
}
int query(char str[])
{
    int p=0;
    for(int i=0;str[];i++)
    {
        int u=str[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[2];
        scanf("%s%s",op,str);
        if(op[0]=='I')insert(str);
        else cout<<query(str)<<endl;
    }
    return 0;
}

并查集

C语言朴素代码

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100

int uset[MAXSIZE];//定义一个足够长的数组
//用下标来表示结点
/*
构造并查集
int size 有多少个节点
*/
void makeSet(int size)
{
    for (int i = 0; i < size; i++)
    {
        //各自为各自的代表
        uset[i] = i;
    }
}

/*
找到元素所在的集合的代表 如果位于同一集合 
*/
int find(int i)
{
    if (i == uset[i])
    {
        return i;
    }
    return find(uset[i]);
}

void unite(int x, int y) 
{
    //先找相对应的代表
    int x = find(x);
    int y = find(y);
    if (x == y)
    {
        return;
    }
    uset[x] = y;

}

C语言路径压缩

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
/*
因为在特殊情况下 这棵树可能是一个长长的树链 设链的最后一个节点为x
每次执行find 相当于遍历整个链条
只需要把便利过的结点都改成跟的子节点 查询就会快很多
*/


int uset[MAXSIZE];//定义一个足够长的数组 每个结点
int rank[MAXSIZE];//树的高度

//用下标来表示结点
/*
构造并查集
int size 有多少个节点
*/
void makeSet(int size)
{
    for (int i = 0; i < size; i++)
    {
        //各自为各自的代表
        uset[i] = i;
        //树的高度
        rank[i] = 0;
    }
}

/*
找到元素所在的集合的代表 如果位于同一集合
查找元素在哪个集合
*/
int find(int i)
{
    if (i == uset[i])
    {
        return i;
    }
    return uset[i] = find(uset[i]);//在第一次查找的时候 将结点直接连到跟
}

void unite(int x, int y)
{
    //先找相对应的代表
    int x = find(x);
    int y = find(y);
    if (x == y)
    {
        return;
    }
    //判断两棵树的高度 在决定谁为子树
    if (rank[x] < rank[y])
    {
        uset[x] = y;
    }
    else
    {
        uset[y] = x;

        if (rank[x] == rank[y])
        {
            rank[x]++;
        }
    }


}

堆排序

  1. 插入一个数 heap[++size]=x;up(size);
  2. 求集合中的最小值 heap[1];
  3. 删除最小值heap[1]=heap[size];size--;down(1)
  4. 删除任意一个元素 heap[k]=heap[size];size--;down(k);up(k);
  5. 修改任意一个元素heap[k]=x;down(k);up(k);

存储方式:完全二叉数(一维数组)左儿子2x,右儿子2x+1

性质:根是最小值

操作:downup

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 100010;
int h[N], ph[N], hp[N], cnt;
void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}
//down:将大数下压
void down(int u)
{
    int t = u;
    if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if (u != t)
    {
        heap_swap(u, t);
        down(t);
    }
}
//up:将小数上移
void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

int main()
{
    int n, m = 0;
    scanf("%d", &n);
    while (n -- )
    {
        char op[5];
        int k, x;
        scanf("%s", op);
        if (!strcmp(op, "I"))
        {
            scanf("%d", &x);
            cnt ++ ;
            m ++ ;
            ph[m] = cnt, hp[cnt] = m;
            h[cnt] = x;
            up(cnt);
        }
        else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
        else if (!strcmp(op, "DM"))
        {
            heap_swap(1, cnt);
            cnt -- ;
            down(1);
        }
        else if (!strcmp(op, "D"))
        {
            scanf("%d", &k);
            k = ph[k];
            heap_swap(k, cnt);
            cnt -- ;
            up(k);
            down(k);
        }
        else
        {
            scanf("%d%d", &k, &x);
            k = ph[k];
            h[k] = x;
            up(k);
            down(k);
        }
    }

    return 0;
}

哈希表

哈希表的存储结构

哈希函数:h(x)=x%1e5

哈希冲突:拉链法

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+3;
int h[maxn],e[maxn],idx,ne[maxn];
void insert(int x)
{
    int k=(x%maxn+maxn)%maxn;
    e[idx]=x;
    ne[idx]=h[k];
    h[k]=idx++;
}
bool find(int x)
{
    int k=(x%maxn+maxn)%maxn;
    for(int i=h[k];i!=-1;i=ne[i])
    {
        if(e[i]==x)return true;
    }
    return false;
}
int main()
{
    int n;cin>>n;
    memset(h,-1,sizeof h);
    string op;int x;
    while(n--)
    {
        cin>>op;
        cin>>x;
        if(op[0]=='I')insert(x);
        else 
        {
            if(find(x))puts("Yes");
            else puts("No");
        }
    }


    return 0;
}

开放寻址法

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+3,null=0x3f3f3f3f;
int h[maxn];
int find(int x)//返回这个值的位置或者可以存放的位置
{
    int t=(x%maxn+maxn)%maxn;
    while(h[t]!=null&&h[t]!=x)
    {
        t++;
        if(t==maxn)t=0;
    }
    return t;
}
int main()
{
    int n;cin>>n;
    memset(h,null,sizeof h);
    string op;int x;
    while(n--)
    {
        cin>>op>>x;
        if(op[0]=='I')h[find(x)]=x;
        else 
        {
            if(h[find(x)]==null)puts("No");
            else puts("");
        }
    }
    return 0;
}

字符串前缀哈希

  1. 不用0当哈希值
  2. 完全不考虑冲突
  3. 经验值:p=131,q=2^60
  4. 实现原理
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int maxn=1e5+10,P=131;
int n,m;
char str[maxn];
ULL h[maxn],p[maxn];
ULL get(int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
    cin>>n>>m;
    cin>>str+1;
    p[0]=1;
    for(int i=1;i<=n;i++)
    {
        h[i]=h[i-1]*P+str[i];
        p[i]=p[i-1]*P;
    }
    while(m--)
    {
        int l1,r1,l2,r2;
        cin>>l1>>r1>>l2>>r2;
        if(get(l1,r1)==get(l2,r2))puts("Yes");
        else puts("No");
    }
    
    return 0;
}

STL使用

vector

变长数组,倍增的思想

vector<int>a(10,3);//定义一个长度为10的vector并初始化为3
a.size();
a.empty();
a.clear();
a.front();a.back();
a.push_back();a.pop_back();
a.begin();a.end();
a[idx];
//为程序分配空间时,所需时间与空间大小无关,只与申请次数有关
//迭代方式
for(int i=0;i<a.size();i++)cout<<a[i]<<" ";
for(vector<int>::iterator i=a.begin();i!=a.end();i++)cout<<*i<<" ";
for(auto x:a)cout<<x<<" ";
//支持比较运算 按字典序

pair

pair<int,string>p;
p.first;p.second;
//支持比较运算,按照字典序,以first为优先级
//初始化
p=make_pair(10,"jhs");
p={20,"jhs"};
pair<int,pair<int,int>>p;

string

字符串

string a="yxc";
a+="def";a+='c';
a.substr(1,2);//返回从下标1开始长度为2的子串
size();empty();clear();
a.c_str();//返回字符数组的起始地址

queue/priority_queue

queue<int>q;
q.size();q.empty();q.push(x);q.front();q.back();q.pop();
//默认大根堆
priority_queue<int>heap;
q.push();q.top();q.pop;
//定义小根堆
q.push(-x);
priority_queue<int,vector<int>,greater<int>>heap;

stack

stack<int>s;
s.push();s.top();s.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();++;--;//返回前驱和后继
//set/mulitset
insert();//插入一个数
find();//查找一个数,返回end的迭代器
count();//返回某一个数的个数
erase();//删除迭代器或者数字
lower_bound(x);//返回大于等于x的最小的数
upper_bound(x);//返回大于x的最小的数
//都是返回迭代器,找不到则返回end
//map/multimap
insert();//插入的数是一个pair
erase();//删除迭代器或者pair
find();
[];//时间复杂度logn

unordered_set/unordered_map/underordered_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位取反

归并排序

模板:

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
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()
{
    int n;
    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;
}

例题:求逆序对的数量:

class Solution {
public:
    int merge(vector<int> &nums,int l,int r){
        if(l>=r)return 0;//当只有一个元素
        int mid=(l+r)>>1;
        int res=merge(nums,l,mid)+merge(nums,mid+1,r);//两边的逆序对数量
        int i=l,j=mid+1;//因为是向下取整,所以要加一
        vector<int>temp;
        while(i<=mid&&j<=r){
            if(nums[i]<=nums[j])temp.push_back(nums[i++]);
            else{
                temp.push_back(nums[j++]);
                res+=mid+1-i;//这里是推导式,当右边的数字归并时,在其前面比其大的数字个数就是mid-1+i-i
            }
        }
        while(i<=mid)temp.push_back(nums[i++]);
        while(j<=r)temp.push_back(nums[j++]);
        i=l;
        for(auto x:temp)nums[i++]=x;//见归并后的结果填入数组
        return res;
    }
    int inversePairs(vector<int>& nums) {
        return merge(nums,0,nums.size()-1);
    }
};
posted @ 2022-10-26 11:16  URMSTAR  阅读(20)  评论(0)    收藏  举报