1.堆
堆其实就是一棵树,不过可用范围主要集中在堆顶
由此 我们拥有了大根堆&小根堆 然而我不会
一道手写堆 体验一下是极好的,尤其是堆的siftdown
洛谷P1878 舞蹈课
附上大佬的ac码(菜如我只能借鉴了qwq)
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
struct node{
int val,l,r;
//重载运算符 靠左优先
//结构体重载运算符要加friend (要用两个变量的话)
friend bool operator>(node n1,node n2){
return n1.val>n2.val || (n1.val==n2.val && n1.l>n2.l);
}
friend bool operator <(node n1,node n2){
return n1.val<n2.val ||(n1.val==n2.val && n1.l< n2.l);
}
//或者像这样
/*
bool operator>(const node& n2) const {
return val>n2.val || (val==n2.val && l>n2.l);
}
bool operator<(const node& n2)const {
return val<n2.val ||(val==n2.val && l< n2.l);
}
*/
} a[2000006];
int n,v[200006],cnt,ansl[200005],ansr[200005],HeapSize;
bool vis[200005],f[200005];
node heap[200005];
string s;
//这里重点就是这个手写堆辣
void insert(int val,int l,int r){
int now=++HeapSize;
int nxt;
//先把他放到最底下 再往上调整
heap[HeapSize].val=val;
heap[HeapSize].l=l;
heap[HeapSize].r=r;
while(now>1){
//右移操作
nxt=now>>1;
//如果碰到的地方已经比他小了 那么它到了自己的位置了
if(heap[now]>heap[nxt]) return ;
//如果没有,那就要往上调整
else swap(heap[now],heap[nxt]);
now=nxt;
}
return ;
//出堆 这是小根堆 堆顶最小
void Get(){
int now=1;
int nxt;
//把堆底的一个元素 覆盖掉顶
//这样堆的起始序号还是从1开始,节约空间
heap[1].val=heap[HeapSize].val;
heap[1].l=heap[HeapSize].l;
heap[1].r=heap[HeapSize].r;
HeapSize--;
//然后把它往下面推 同理
while(now<< 1 <= HeapSize){
nxt=now<<1;
//这个二分的操作真的好神奇呀
//保持左右子树的稳定性 换到右子树上
if(nxt< HeapSize && heap[nxt]>heap[nxt+1]) nxt++;
if(heap[nxt]>heap[now]) return ;
else swap(heap[now],heap[nxt]);
now=nxt;
}
return;
}
void print(){
cout << cnt <<endl;
for(int i=1;i<=cnt;i++){
cout << ansl[i]<<" "<<ansr[i]<<endl;
}
return ;
}
int main(){
cin >>n;
cin >> s;
for(int i=1;i<=n;i++)
cin>>v[i];
for(int i=1;i<=n;i++)
f[i]=(s[i-1]=='B')? 1: 0;
for(int i=1;i<=n;i++){
int nxt=i+1;
if(nxt<=n && f[i]!=f[nxt]){
insert(abs(v[i]-v[nxt]),i,nxt);
}
}
while(HeapSize){
int l=heap[1].l;
int r=heap[1].r;
int curl,curr;
vis[l]=1;vis[r]=1;
ansl[++cnt]=l;
ansr[cnt]=r;
do{
Get();
if(!HeapSize) break;
//当前的值
curl=heap[1].l;
curr=heap[1].r;
}
while(vis[curl] || vis[curr]);
while(l && vis[l]) l--;
while(r<=n && vis[r]) r++;
if(l && r <=n && f[l]!=f[r])insert(abs(v[l]-v[r]),l,r);
}
print();
return 0;
}
2.树
(1)一般都是二叉树
- 满二叉树与完全二叉树的辨别:
嘤 满二叉树一定和完全二叉树没有必然联系8,
(Full binary tree) 满二叉树要求每个非叶子结点都要有左右儿子。
(Complete binary tree) 完全二叉树要求排布顺序,要求严格按照左根右的顺序出现节点。
这种 看图就好了(xx
(2)基本操作 声明,建树,遍历,查询和插入(都是基础的维护操作)
- 插播一个树的遍历 分为前序中序后序,意思就是在访问节点时根节点的访问次序: 前序:根左右 中序:左根右 后序:左右根 挺好记住的,以及遍历的时候递归地按照这个规则就好辣
- 知识点:霍夫曼编码树(很有意思 值得一学) 可以优化取字母的效率
*两个板子(链表版+数组版)
以及放个题 巨佬们的树写的很漂亮呜呜呜
果然还是不喜欢链表版的树
线段树—查找最后一个大于指定元素的位置
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
//en,题意相当清晰
//求ai右边最后一个至少比ai大m的数与它的距离
//线段树
const int maxn=500005;
int n,m;
int x[maxn],ans[maxn],w[maxn],Tree[maxn<<2];
void pushup(int rt){
Tree[rt]=max(Tree[rt<<1|1],Tree[rt<<1]);
}
void update(int pos,int val,int l,int r,int rt){
if(l==r){
Tree[rt]=val;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(pos,val,l,mid,rt<<1);
else update(pos,val,mid+1,r,rt<<1|1);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&R>=r){
return Tree[rt];
}
int ans=0;
int mid=(l+r)>>1;
if(mid>=L) ans=max(ans,query(L,R,l,mid,rt<<1));
if(mid<=R) ans=max(ans,query(L,R,mid+1,r,rt<<1|1));
return ans;
}
int main(){
cin >> n >>m;
for(int i=1;i<=n;i++){
cin>>w[i];
x[i]=w[i];
}
sort(x+1,x+1+n);
//去重要记得多减去一个1 qwq
int len=unique(x+1,x+1+n)-x-1;
for(int i=1;i<=n;i++){
int pos=lower_bound(x+1,x+1+len,w[i])-x;
update(pos,i,1,len,1);
}
for(int i=1;i<=n;i++){
int pos=lower_bound(x+1,x+1+len,w[i]+m)-x;
//According to the question,dis=r-l-1.
ans[i]=query(pos,len,1,len,1)-i-1;
if(ans[i]<0) ans[i]=-1;
}
for(int i=1;i<n;i++)cout << ans[i]<<" ";
cout << ans[n]<<endl;
return 0;
}
板子 勉强能看懂了 什么时候能自己写个啊qwq
找后l个元素中最大的那个-线段树可写
#include<iostream>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int inf = 0x3f3f3f;
int n,q[12345678],t=0,root=1,m=1,inputi;
long long d=0;
char inputc;
void build(int l,int r,int rt){
q[rt]=-inf;
if(l==r) return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
void plusa(int l,int r,int rt,int p,int v){
if(l==r) {
q[rt]=v;
return ;
}
int m=(l+r)>>1;
if(p<=m) plusa(l,m,rt<<1,p,v);
else plusa(m+1,r,rt<<1|1,p,v);
q[rt]=max(q[rt<<1],q[rt<<1|1]);
}
int sth;
int ask(int l,int r,int rt,int nl,int nr){
if(nl<=l&&nr>=r) return q[rt];
int m=(l+r)>>1;
int minn=-inf;
if(nl<=m) minn=ask(l,m,rt<<1,nl,nr);
if(nr>m) minn=max(minn,ask(m+1,r,rt<<1|1,nl,nr));
return minn;
}
int main(){
cin >> m>>d;
build(1,m,1);
for(int i=1;i<=m;i++){
cin>>inputc>>inputi;
if(inputc=='A'){
plusa(1,m,1,++sth,(inputi+t)%d);
}
else{
t=ask(1,m,1,sth-inputi+1,sth);
cout<<t<<endl;
}
}
return 0;
}
给个链表版的BST&一个霍夫曼树qwq
真实写了好久(显然是借鉴大佬们的
数组的设置非常弱智 有空再改啊,其实是多余的
(可以实现从txt中读取一串不区分大小写的字母,进行霍夫曼编码并输出)
*BST
#include<iostream>
using namespace std;
/*建立一个含有n个结点的二叉检索树,使用指针实现,并在此基础上分别设计一个主程序完成如下功能:
- 通过键盘输入两个结点序列分别构建两个二叉检索树,要求结点值在0~100之间,结点数量不少于8个;
- 输出该二叉检索树的顺序表示(见教材6.5节,"/"代表空指针NULL);
- 输出对该树的前序遍历、中序遍历、后续遍历的结果;观察各遍历输出结果,哪一种遍历将结点按照从小到大进行了排列;
- 输出该树的叶子结点的数量及叶子结点的值;
*/
int a[400],b[400];
//int a[40]={0 15 54 676 3 777 6 7 8 34 2 5};
//int b[40]={0 18 9 10 4 2 3 68 30 23 66};
int cnt=0;
int leaf[40];
struct node{
int data;
node* lc;
node* rc;
};
node * rt1=NULL;
node * rt2=NULL;
node * newNode(int v){
node * Node = new node;
Node->data=v;
Node->lc = Node->rc = NULL;
return Node;
}
void insert(node* &now,int x){
if(now==NULL){
now=newNode(x);
return;
}
if(now->data == x){
return;
}
else if(x< now->data){
insert(now->lc,x);
}
else insert(now->rc,x);
}
node* Build(int data[],int n,node* &rt){
for(int i=1;i<=n;i++){
insert(rt,data[i]);
}
return rt;
}
void preorder(node *now){
if(now==NULL) return;
cout<<now->data<<" ";
preorder(now->lc);
preorder(now->rc);
return;
}
void inorder(node *now){
if(now==NULL) return ;
inorder(now->lc);
cout<<now->data<<" ";
inorder(now->rc);
return ;
}
void postorder(node *now){
if(now==NULL) return ;
postorder(now->lc);
postorder(now->rc);
cout<<now->data<<" ";
return;
}
void Print(node *now){
if(now==NULL) {
cout<<"/ ";
return;
}
cout<<now->data<<" ";
Print(now->lc);
Print(now->rc);
return ;
}
void CountLeaf(node *now){
if(now==NULL) return ;
if(!now->lc && !now->rc){
leaf[++cnt]=now->data;
}
CountLeaf(now->lc);
CountLeaf(now->rc);
return ;
}
void test(node * &rt,int aa[],int l){
Build(aa,l,rt);
cout<<endl<<"【前序遍历】:Preorder trasveral: ";
preorder(rt);
cout<<endl<<"【中序遍历】:Inorder trasversal: ";
inorder(rt);
cout<<endl<<"【后序遍历】:Postorder traversal: ";
postorder(rt);
cout<<endl;
CountLeaf(rt);
cout<<endl<<"【输出叶子节点】"<<endl<<"This BST has "<<cnt<<" LeafNodes, there are: ";
for(int i=1;i<=cnt;i++){
cout << leaf[i]<<" ";
}
cout<<endl;
//for(int i=1;i<=cnt;i++)cout<<"(";
cout<<endl<<"【顺序表示法】: ";
Print(rt);
cout<<endl;
}
void ini(){
for(int i=1;i<=cnt;i++)leaf[i]=0;
cnt=0;
}
int main(){
int t=2;
while(t--){
int x;
cout<<"Print the number"<<endl;
cin>>x;
for(int i=1;i<=x;i++){
cin>>a[i];
}
node *newrt = new node;
newrt=NULL;
cout<<"The first BST:"<<endl;
test(newrt,a,x);
ini();
for(int i=1;i<=100;i++)cout<<'-';
cout<<endl;
}
return 0;
}
*霍夫曼
#include<iostream>
#include<fstream>
#include<string.h>
using namespace std;
//input from a txt
fstream fin;
/*
int f[50]={0,7,9,2,6,32,3};
char a[50]={'A','B','C','D','E','F','G'};
*/
int count=0;
int cnt[50],t[50];
char word[50];
int tmp[1000];
struct HNode{
int w;
char c;
HNode *lc,*rc;
//if it is in the queue
//if used, will not use it again
bool flag;
HNode(){
flag=1;
w=0;
c='0';
lc=rc=NULL;
}
//initiate
HNode(int ww,char cc,HNode* l,HNode *r):w(ww),c(cc),lc(l),rc(r),flag(1){
};
};
class Huffman{
public:
HNode *rt;
HNode* forest[5000];
int cnt;
void Build(int f[],char a[],int size){
int k=0;
for(int i=1;i<=size;i++){
HNode *newnode = new HNode(f[i],a[i],NULL,NULL);
forest[i]=newnode;
}
for(int i=1;i<size;i++){
//cout<<forest[i]->w<<endl;
//sort the tree leaves in order
newsort(size+k);
for(int j=1;;j++){
if(forest[j]->flag==1&&forest[j+1]->flag==1){
HNode *newnode = new HNode(forest[j]->w+forest[j+1]->w,'0',forest[j],forest[j+1]);
k++;
forest[size+k]=newnode;
//cout<<forest[j]->c<<" and "<<forest[j+1]->c<<endl;
//cout<<"new node w: "<<newnode->w<<endl;
forest[j]->flag=0;
forest[j+1]->flag=0;
break;
}
else continue;
}
}
rt=forest[size+k];
}
//if we find the character we need, output the answer(code of it)
void PrintAns(char c,int x){
cout<<" ";
for(int i=1;i<x;i++)
cout<<tmp[i];
return;
}
void printCode(HNode *now,char t,int x){
if(now==NULL) return;
if(now->c == t){
PrintAns(t,x);
}
//book the answer
tmp[x]=0;
printCode(now->lc,t,x+1);
tmp[x]=1;
printCode(now->rc,t,x+1);
return;
}
void inorder(HNode* now){
if(now!=NULL){
inorder(now->lc);
cout<<now->w<<" ";
//if(now->c>='A'&&now->c <='Z')
//cout<<now->c<<" ";
//cout<<now->w<<" ";
inorder(now->rc);
}
}
void newsort(int size){
//Bubble5sort
for(int i=1;i<size;i++)
{
for(int j=i+1;j<=size;j++){
if(forest[j]->w < forest[i]->w){
swap(forest[i],forest[j]);
}
}
}
}
private:
protected:
};
//process the input
void process(string x){
int l=x.length();
for(int i=0;i<l;i++)
if(x[i]>='a'&&x[i]<='z')
x[i]=char(x[i]+'A'-'a');
for(int i=0;i<l;i++)
t[x[i]-'A'+1]++;
}
int main(){
cout<<"Input a text as follows:"<<endl<<endl;
fin.open("data.txt");
while(fin){
string x;
fin>>x;
cout<<x<<endl;
process(x);
}
cout<<"Frequency:"<<endl<<endl;
for(int i=1;i<=26;i++){
if(t[i]){
word[++count]=char(i+'A'-1);
cnt[count]=t[i];
cout<<" "<<word[count]<<" :"<<cnt[count]<<endl;
}
}
cout<<endl;
Huffman Tree;
for(int i=1;i<=10;i++)cout<<'-';
cout<<endl<<"Codes:"<<endl;
Tree.Build(cnt,word,count);
//for every char appears in the tree, output the codes.
for(int i=1;i<=count;i++){
cout<<" ("<<i<<")"<<word[i]<<":";
Tree.printCode(Tree.rt,word[i],1);
cout<<endl;
}
return 0;
}