目录
Stack
stack<int> s;
s.push();//向栈顶添加元素
s.pop();//从栈顶移除一个元素
s.top();//返回栈顶元素
s.empty();//判断堆栈是否为空
s.size();//返回栈的大小
单调栈:
#include<stack>
#include<vector>
ll n,h,ans,th,tw,w;
int main() {
while(scanf("%lld",&n)) {
if(!n)break;
stack<pair<ll,ll> >stk;
ans=0;
for(int i=0; i<n; i++) {
scanf("%lld",&h);
w=0;
while(!stk.empty()&&stk.top().first>=h) {
th=stk.top().first;
tw=stk.top().second;
stk.pop();
w+=tw;
ans=max(ans,th*w);
}
stk.push(make_pair(h,w+1));
}
w=0;
while(!stk.empty()) {
ans=max(ans,stk.top().first*(w+stk.top().second));
w+=stk.top().second;
stk.pop();
}
printf("%lld\n",ans);
}
return 0;
}
一个序列有多少出栈顺序:
#include<queue>
#include<stack>
using namespace std;
const int N=1e5+10;
int a[10],n;
stack<int>s;
queue<int>q;
bool check(queue<int>q)///判断是否满足要求
{
for(int i=1; i<=n; i++)
{
s.push(a[i]);///栈不为空&&栈顶元素和队列首元素相同
while(!s.empty()&&q.front()==s.top())
{
s.pop();///出栈
q.pop();///出队列
}
}
if(!s.empty())///判断栈中是否还有元素
return false;///有元素,说明不是出栈顺序
return true;///没有,则说明是出栈顺序
}
int main()
{
int k=1;///记录全排列的个数
printf("请输入入栈长度\n");
scanf("%d",&n);
printf("请输入入栈顺序\n");
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);///全排列排序
do{
printf("Case %d:",k++);///第k个全排列
for(int i=1; i<=n; i++)
{
printf("%d ",a[i]);
q.push(a[i]);///全排列的一种入队
}
printf("\n");
if(check(q))///判断入栈序列的全排列是否是出栈顺序
printf("是出栈顺序\n");
else printf("不是出栈顺序\n");
}while(next_permutation(a+1,a+n+1)); ///全排列函数调用
return 0;}
Queue
///基本操作:push();pop();front(队首);back(队尾);empty();size();
///priority_queue(优先队列)
///出队时,并非按先进先出的原则,而是将当前队列中最大的元素出队(默认由大到小排序)。可以重载"<"操作,重新定义
//结构体重载:
struct node {
string name;
float score;
bool operator < (const node &a) const {
return a.score<score;
}
}
int main() {
priority_queue<node>pq;
return 0;
}
///非结构体重载:
struct myComp() {
bool operator () (const int &a,const int &b) {
///由小到大排列采用">";有小到大排列采用"<"
return a>b;
}
}
int main() {
priority_queue<int,vector<int>,myComp>pq;
return 0;
}
- priority_queue
priority_queue< type, container, function > ///默认是一个最大堆
type:数据类型;(不可省)
container:实现优先队列的底层容器;
function:元素之间的比较方式;
//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;
//另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;
//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;
priority<node>pq_node //node为结构体,可以自定义优先级
struct node{
int x, y;
friend bool operator < (node a, node b){
return a.x > b.x; //结构体中,x小的优先级高
}
}
#include <functional>
Map
每个关键字只能出现一次,根据key值快速查找记录,查找的复杂度基本是Log(N)
插入元素:
map<int, string> mapStudent; // 定义一个map对象
//用insert函數插入pair
mp.insert(pair<int, string>(000, "student_zero"));
//用insert函数插入value_type数据
mp.insert(map<int, string>::value_type(001, "student_one"));
mp[123] = "student_first";//用"array"方式插入
mp[456] = "student_second";
插入是否成功:
// 构造定义,返回一个pair对象
pair<iterator,bool> insert (const value_type& val);
pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mp.insert(map<int, string>::value_type (001, "student_one"));
if(!Insert_Pair.second)
cout << ""Error insert new element" << endl;
数据的遍历:
map<int, string>::iterator it;
for(it = mp.rbegin(); it != mp.rend();it++)
cout<<it->first<<" "<<it->second<<endl; ///前向迭代器
map<int, string>::reverse_iterator it;
for(it = mp.rbegin(); it != mp.rend();it++)
cout<<it->first<<" "<<it->second<<endl; ///反相迭代器
for(int i = 1;i <= n;i++) ///注意从1开始
cout<<mp[i]<<endl; ///数组的形式
查找并获取map中的元素
count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,
find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器。
删除与清空:
//迭代器刪除
iter = mp.find("123");
mp.erase(iter);
//用关键字刪除
int n = mp.erase("123"); //如果刪除了會返回1,否則返回0
//用迭代器范围刪除 : 把整个map清空
mp.erase(mp.begin(), mp.end());
//等同于mp.clear()
map中的swap用法:
swap不是一个容器中的元素交换,而是两个容器所有元素的交换。
函数:
函数 | 作用 |
---|---|
begin() | 返回指向map头部的迭代器 |
clear() | 删除所有元素 |
count() | 返回指定元素出现的次数 |
empty() | 如果map为空则返回true |
end() | 返回指向map末尾的迭代器 |
rbegin() | 返回一个指向map尾部的逆向迭代器 |
rend() | 返回一个指向map头部的逆向迭代器 |
size() | 返回map中元素的个数 |
swap() | 交换两个map |
upper_bound() | 返回键值>给定元素的第一个位置 |
链表
///单链表中可以没有头结点,但是不能没有头指针!
///结构体实现自定义:
typedef struct Link {
int elem;
struct Link *next;
} link;
link * initLink();
//链表插入的函数,p是链表,elem是插入的结点的数据域,add是插入的位置
link * insertElem(link * p,int elem,int add);
//删除结点的函数,p代表操作链表,add代表删除节点的位置
link * delElem(link * p,int add);
//查找结点的函数,elem为目标结点的数据域的值
int selectElem(link * p,int elem);
//更新结点的函数,newElem为新的数据域的值
link *amendElem(link * p,int add,int newElem);
void display(link *p);
int main() {
//初始化链表(1,2,3,4)
printf("初始化链表为:\n");
link *p=initLink();
display(p);
printf("在第4的位置插入元素5:\n");
p=insertElem(p, 5, 4);
display(p);
printf("删除元素3:\n");
p=delElem(p, 3);
display(p);
printf("查找元素2的位置为:\n");
int address=selectElem(p, 2);
if (address==-1) {
printf("没有该元素");
} else {
printf("元素2的位置为:%d\n",address);
}
printf("更改第3的位置的数据为7:\n");
p=amendElem(p, 3, 7);
display(p);
return 0;
}
link * initLink() {
link * p=(link*)malloc(sizeof(link));//创建一个头结点
link * temp=p;//声明一个指针指向头结点,用于遍历链表
//生成链表
for (int i=1; i<5; i++) {
link *a=(link*)malloc(sizeof(link));
a->elem=i;
a->next=NULL;
temp->next=a;
temp=temp->next;
}
return p;
}
link * insertElem(link * p,int elem,int add) {
link * temp=p;//创建临时结点temp
//首先找到要插入位置的上一个结点
for (int i=1; i<add; i++) {
if (temp==NULL) {
printf("插入位置无效\n");
return p;
}
temp=temp->next;
}
//创建插入结点c
link * c=(link*)malloc(sizeof(link));
c->elem=elem;
//向链表中插入结点
c->next=temp->next;
temp->next=c;
return p;
}
link * delElem(link * p,int add) {
link * temp=p;
//遍历到被删除结点的上一个结点
for (int i=1; i<add; i++) {
temp=temp->next;
}
link * del=temp->next;//单独设置一个指针指向被删除结点,以防丢失
temp->next=temp->next->next;//删除某个结点的方法就是更改前一个结点的指针域
free(del);//手动释放该结点,防止内存泄漏
return p;
}
int selectElem(link * p,int elem) {
link * t=p;
int i=1;
while (t->next) {
t=t->next;
if (t->elem==elem) {
return i;
}
i++;
}
return -1;
}
link *amendElem(link * p,int add,int newElem) {
link * temp=p;
temp=temp->next;//tamp指向首元结点
//temp指向被删除结点
for (int i=1; i<add; i++) {
temp=temp->next;
}
temp->elem=newElem;
return p;
}
void display(link *p) {
link* temp=p;//将temp指针重新指向头结点
//只要temp指针指向的结点的next不是Null,就执行输出语句。
while (temp->next) {
temp=temp->next;
printf("%d",temp->elem);
}
printf("\n");
}
哈希表
直接定址法
除留余数法(Hash表的最大长度为m,可以取不大于m的最大质数p,然后对关键字进行取余运算)
并查集
const int N=2e5+10;
int f[N],rankk[N];
int find(int x)
{
if(f[x]==-1)
return x;
rankk[x]+=rankk[(f[x])];
/// 由于每条边带权,所以把边权更新,
/// 也就是更新间接连接的点;当递归到某一层时
/// x还未连接到根节点上所以rank[x]表示的是x到f[x]的距离;
/// 经上一步操作,f[x]已经接到根节点上了,
/// 即rank[f[x]]表示的是父节点到根节点的距离所以x到根节点的距离就直接
/// 等于rank[x]+rank[f[x]];
int t=find(f[x]);
return f[x]=t;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
memset(rankk,0,sizeof(rankk));
memset(f,-1,sizeof(f));
int ans=0;
int a,b,sum;
for(int i=0; i<m; i++)
{
scanf("%d %d %d",&a,&b,&sum);
a--;///区间(0,b)分为(0,a-1)和(a,b);
int fx=find(a);
int fy=find(b);
if(fx!=fy)
{
f[fy]=fx;
rankk[fy]=rankk[a]-rankk[b]+sum;
///rank[a]表示a到0的和,rank[b]表示a+1到b的和;
///rank[fy]表示fx,fy的距离;
}
else if(rankk[b]-rankk[a]!=sum)
ans++;
}
printf("%d\n",ans);
}
return 0;
}
树状数组
int n;
int bit_tree[n];
void add(int a,int b){
for(int i=a;i<=n;i+=lowbit(i)){
bit_tree[i]+=b;
}
}//这里每次修改的其实是一个后缀
void add_area(int l,int r,int v){
add(l,v);add(r+1,-v);//差分后的修改方式,由于只会影响l~r,所以将r后的影响要消除
}
int query_pos(int a){
int ans=0;
for(int i=a;i>0;i-=lowbit(i)){
ans+=bit_tree[i];
}
return ans;
}
区间最大
int max_area(int l,int r){
int ans=0;
while(l<=r){
ans=max(ans,val[r]);r--;//每次往后跳一个
for(;r-lowbit(r)>=l;r-=lowbit(r)) ans=max(ans,maxv[r]);//看r最多跳到哪里,而不超过l
}
return ans;
}
int change_pos(int p,int a){
val[p]=a;
for(int i=p;i<=n;i+=lowbit(i)){
maxv[i]=val[i];//修改的时候重新计算值
for(int j=1;j<lowbit(i);j<<=1) maxv[i]=max(maxv[i],maxv[i-j]);
}
}
void init(int p){
for(int i=p;i<=n;i+=lowbit(i)) maxv[i]=max(maxv[i],val[p]);//初始化一个位置的值可以这样写。
}
线段树
char str[10];
long long int lazy[400040],sum[400040];///4倍的空间
void build(int l,int r,int o)///建树 l,r 此节点区间长度 o下标
{
if(l==r)///叶节点
{
scanf("%lld",&sum[o]);///直接赋值
return;
}
int mid=(l+r)>>1;
build(l,mid,o<<1);///左
build(mid+1,r,o<<1|1);///右
sum[o]=sum[o<<1]+sum[o<<1|1];///更新此节点的和
}
void pushdown(int o,int l)///o下标 l此节点的区间长度
{
if(lazy[o])///如果此时需要更新操作
{
lazy[o<<1]+=lazy[o];///左
lazy[o<<1|1]+=lazy[o];///右
sum[o<<1]+=lazy[o]*(l-(l>>1));///更新此时 左 的和
sum[o<<1|1]+=lazy[o]*(l>>1);///更新此时 右 的和
lazy[o]=0;///此点位置懒惰标记取消 传到下面两个节点
}
}
void update(int x,int y,int l,int r,int o,int c)///update(l,r,1,n,1,c);
{///x,y操作区间 l,r树的范围 o下标 c具体操作(+c)
if(x<=l&&y>=r)///操作区间全在树的范围内
{
sum[o]+=(r-l+1)*c;///对此时sum[o]进行更新操作
lazy[o]+=c;///并懒惰标记
return ;
}
pushdown(o,r-l+1);///不符合 就push懒惰标记
int mid=(l+r)>>1;
if(x<=mid)update(x,y,l,mid,o<<1,c);///更新左
if(y>mid)update(x,y,mid+1,r,o<<1|1,c);///更新右
sum[o]=sum[o<<1]+sum[o<<1|1];///更新此节点的和
}
long long int query(int x,int y,int l,int r,int o)
{///x,y操作区间 l,r树的范围 o下标
if(x<=l&&y>=r)///全包含 就输出此时区间的和
return sum[o];
pushdown(o,r-l+1);///懒惰标记函数
int mid=(r+l)>>1;
long long sum=0;///long long型
if(x<=mid)sum+=query(x,y,l,mid,o<<1);///说明 树的左边与操作区间有交集
if(y>mid)sum+=query(x,y,mid+1,r,o<<1|1);///o<<1|1 位运算比+-快
return sum;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
memset(lazy,0,sizeof(lazy));
memset(sum,0,sizeof(sum));
build(1,n,1);///建树
while(m--)
{
int l,r,c;
scanf("%s",&str);///字符串避免回车符
if(str[0]=='Q')
{
scanf("%d %d",&l,&r);
printf("%lld\n",query(l,r,1,n,1));///查询函数
}
else
{
scanf("%d %d %d",&l,&r,&c);
update(l,r,1,n,1,c);///操作更新函数
}
}
}
return 0;
}
区间操作
题意:给定n个线段,线段可以相交,第i个线段覆盖的区间为[li,ri],问最少删除多少个线段让覆盖每个点的线段数量小于等于k。
#include<vector>
#include<set>
typedef long long ll;
const int N=2e5+10;
using namespace std;
struct node
{
int y;
int id;
};
bool operator<(node a,node b)
{
if(a.y!=b.y)return a.y<b.y;
return a.id<b.id;
}
vector<node>g[N];
vector<int>a;
node q;
int main()
{
int x,y,n,k;
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&x,&y);
q.y=y;q.id=i;
g[x].push_back(q);
}
set<node>s;
for(int i=1;i<N;i++)
{
while(s.size()&&(*s.begin()).y<i)
s.erase(*s.begin());
for(int j=0;j<g[i].size();j++)
s.insert(g[i][j]);
while(s.size()>k)
{
a.push_back((*s.rbegin()).id);
s.erase(*s.rbegin());
}
}
printf("%d\n",a.size());
int l=a.size();
for(int i=0;i<l;i++)
printf("%d%c",a[i],i==l-1?'\n':' ');
return 0;
}
二叉搜索树
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。
#include<bits/stdc++.h>
using namespace std;
#define N 50
#include<queue>
queue<int>qu;
int f[N],m[N];
struct node {
int l,r;
} q[N];
int root;
int build(int la,int ra,int lb,int rb) { //mid first
if(la>ra)
return 0;
int i=0;
int rt=f[lb];///find root
while(m[i]!=rt)
i++;
q[rt].l=build(la,i-1,lb+1,lb+i-la);///left
q[rt].r=build(i+1,ra,lb+i-la+1,rb);///right
return rt;
}
void dfs(int root) {
if(q[root].l==q[root].r&&q[root].l==0)
return ;
else {
swap(q[root].l,q[root].r);///swap
dfs(q[root].l);
dfs(q[root].r);
}
}
void bfs(int root) {
int flag=0;
qu.push(root);
while(!qu.empty()) {
int x=qu.front();
qu.pop();
if(!flag)
flag=1,cout<<x;
else cout<<" "<<x;
if(q[x].l!=0)///exist->input queue
qu.push(q[x].l);
if(q[x].r!=0)
qu.push(q[x].r);
}
cout<<endl;
}
int main() {
int n;
cin>>n;
for(int i=1; i<=n; i++) {
cin>>m[i];
}
for(int i=1; i<=n; i++) {
cin>>f[i];
}
root=build(1,n,1,n);
dfs(root);///swap
bfs(root);///input
return 0;
}
题意:先给一组数据构建一颗二叉搜索树作为标准树。然后n组数据,判断每组数据构成的二叉搜索树是否和标准树一样。
思路:两棵树如果一样的话,就是拥有一样的节点,在每个节点上具有相同的值。
因此在遍历二叉树的时候,每向下移动一个节点时,判断是否与此时的标准树一致。
#include<stdio.h>
#include<string.h>
bool flag;
struct node {
int val;
node *l,*r;
};
node *insert(node *p,int x) { ///依次插入每个值
if(p==NULL) { ///节点为空,插入
node *q=new node;///申请一个动态空间 new node
q->val=x;///赋值
q->l=q->r=NULL;///左右为空
return q;///返回此节点地址
} else { ///节点不为空 此节点有值
if(p->val>x)///当前值小于节点值
p->l=insert(p->l,x);///递归向下寻找对应位置 回溯赋值
else
p->r=insert(p->r,x);
return p;
}
}
void check(node *root,node *room) { ///中序遍历 传入标准树和判断树的地址
if(root!=NULL&&room!=NULL) { ///都不为空 判断值是否相同
check(root->l,room->l);///中序遍历
if(root->val!=room->val)///不一致 标记为0;
flag=0;
check(root->r,room->r);
} else if(root!=NULL||room!=NULL) ///一个为空另一个不为空有节点值 显然不一致
flag=0;
}
int main() {
int n;
while(~scanf("%d",&n)&&n) {
node *root=NULL;///标准树 为空
char str[20];
scanf("%s",str);
for(int i=0; i<strlen(str); i++)///构建 标准树
root=insert(root,str[i]-'0');
for(int i=0; i<n; i++) {
flag=1;///标记
node *room=NULL;///判断树 为空
scanf("%s",str);
for(int j=0; j<strlen(str); j++)///构建 判断树
room=insert(room,str[j]-'0');
check(root,room);///中序(左根右)遍历 判断两棵树是否一致
if(flag)printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
快排
void ksort(int l, int h, int a[]) {
if (h < l + 2) {
return ;
}
int e = h, p = l;
while (l < h) {
while (++l < e && a[l] <= a[p]);
while (--h > p && a[h] >= a[p]);
if (l < h) {
swap(a[l], a[h]);
}
}
swap(a[h], a[p]);
ksort(p, h, a);
ksort(l, e, a);
return ;
}
平衡树- treap
operator 1 : 插入一个数
operator 2 : 删除一个数
operator 3 : 通过数值找排名
operator 4 : 通过排名找数值
operator 5 : 找到严格小于key的最大数(前驱)
operator 6 : 找到严格大于key的最小数(后继)
const int N = 100010, INF = 1e8 + 7;
int n, m;
struct node {
int l, r;//左右儿子
int key;//真正的权值
int val;//随机的平衡因子
int cnt;//相同的数有多少个
int size;//整棵树的结点个数
} tr[N];
int root, idx;
void pushup(int p) {
tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
}
int get_node(int key) {
tr[ ++ idx].key = key;
tr[idx].val = rand();//!堆所维护的val是随机值,为了让树变得更随机以增加搜索树效率
tr[idx].cnt = tr[idx].size = 1;
return idx;
}
/*!往左递归时左儿子val大于根节点val就右旋*/
/*!往右递归时右儿子val大于根节点val就左旋*/
void zig(int &p) { //右旋
int q = tr[p].l;
tr[p].l = tr[q].r;
tr[q].r = p, p = q;
pushup(tr[p].r), pushup(p);
}
void zag(int &p) { //左旋
int q = tr[p].r;
tr[p].r = tr[q].l;
tr[q].l = p, p = q;
pushup(tr[p].l), pushup(p);
}
void build() {
get_node(-INF), get_node(INF);//两个哨兵边界
root = 1;//-INF
tr[root].r = 2;//INF
pushup(root);
if(tr[1].val < tr[2].val)zag(root);
//因为val是随机的,所以要判断右儿子(因为当前只有两个点,只有root右儿子)
//如果右儿子随机到的val大于root就左旋
}
/*operator 1 : 插入一个数*/
void insert(int &p, int key) {
if(!p) p = get_node(key);//如果树中没有这个就新建一个结点
else if(tr[p].key == key) tr[p].cnt ++ ;
else if(tr[p].key > key) { //大就在左边
insert(tr[p].l, key);
if(tr[tr[p].l].val > tr[p].val)zig(p);
} else { // 否则就在右边
insert(tr[p].r, key);
if(tr[tr[p].r].val > tr[p].val)zag(p);
}
pushup(p);
}
/*operator 2 : 删除一个数*/
void remove(int &p, int key) { //删除操作:每次旋转都会使得它的深度+1,一直旋转到叶子节点,然后删除
if(!p) return ;
if(tr[p].key == key) { //找到了
if(tr[p].cnt > 1) tr[p].cnt -- ;
else if(tr[p].l || tr[p].r) {
//!右旋往右走
if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val) {
//如果没有右子树,右旋一次就到叶子节点了。或者说左子树随机到的val > 右子树随机到的val,那么必须右旋
zig(p);
remove(tr[p].r, key);
}
//!左旋往左走
else {
zag(p);
remove(tr[p].l, key);
}
} else p = 0; //如果到了叶子节点就直接删掉
} else if(tr[p].key > key) remove(tr[p].l, key); //往左边找
else remove(tr[p].r, key);//往右边找
pushup(p);//每次别忘了pushup,因为本题中维护了一个size
}
//下面的几个函数只是查询不需要修改,所以不用写&p
/*operator 3 : 通过数值找排名 */
int get_rank_by_key(int p, int key) {
if(!p) return 0;
//如果找到了,返回左子树个数 + 自己(这里不是cnt因为根据题意,要找的排名如果相同就找最左边的,最小的)
if(tr[p].key == key) return tr[tr[p].l].size + 1;
if(tr[p].key > key)
return get_rank_by_key(tr[p].l, key);
return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
}
/*operator 4 : 通过排名找数值 */
int get_key_by_rank(int p, int rank) {
if(!p) return INF;
if(tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
if(tr[tr[p].l].size + tr[p].cnt >= rank)//左边少加上中间的却大于rank,说明就在中间
return tr[p].key;
return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);//注意递归到右边时rank要减去左边的
}
/*operator 5 : 找到严格小于key的最大数 */
int get_prev(int p, int key) {
if(!p) return -INF;
//!左边
if(tr[p].key >= key) return get_prev(tr[p].l, key);
//右边和中间
return max(tr[p].key, get_prev(tr[p].r, key));
}
/*operator 6 : 找到严格大于key的最小数 */
int get_next(int p, int key) {
if(!p) return INF;
//!右边
if(tr[p].key <= key) return get_next(tr[p].r, key);
//左边和中间
return min(tr[p].key, get_next(tr[p].l, key));
}
int main() {
build();
scanf("%d", &n);
while(n -- ) {
int op, x;
scanf("%d%d", &op, &x);
if(op == 1)insert(root, x);
else if(op == 2)remove(root, x);
else if(op == 3)printf("%d\n", get_rank_by_key(root, x) - 1);//因为最前面有一个自己设的哨兵,所以得到的rank比真实的rank大1
else if(op == 4)printf("%d\n", get_key_by_rank(root, x + 1));//根据rank找key,前面有一个哨兵,所以rank要从真实的rank+1变成树里的rank
else if(op == 5)printf("%d\n", get_prev(root, x));
else printf("%d\n", get_next(root, x));
}
return 0;
}
拓扑排序
///优先队列加拓扑排序 入度为零即输出!
int ans[550],ru[550];
int dp[550][550];
int n,m;
void toposort() {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(dp[i][j])
ru[j]++;
}
}
for(int i=1; i<=n; i++) {
int k=1;
while(ru[k]!=0)k++;
ans[i]=k;
ru[k]=-1;
for(int j=1; j<=n; j++) {
if(dp[k][j])
ru[j]--;
}
}
}
最短路
1.floyed
void floyed()
{
for (int k=1;k<=n;k++;)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (dis[i][k]+dis[k][j]<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
2.dijkstra
其实是贪心,不能有负环,如果源点到某个点的距离是到其他点的距离的最小值,能么就更新。
void dijkstra(int st)
{
for (int i=1;i<=n;i++)
dis[i]=a[st][i];
memset(vis,0,sizeof(vis));
vis[st]=1;
dis[st]=0;
for (int i=1;i<n;i++)
{
int minn=99999999;
int k=0;
for (int j=1;j<=n;j++)
if (!vis[j]&&dis[j]<minn)
{
minn=dis[j];
k=j;
}
if (!k)
return ;
vis[k]=1;
for (int j=1;j<n;j++)
if (!vis[j]&&dis[k]+a[c][j]<dis[j])
dis[j]=dis[k]+a[k][i];
}
}
3.bellman_ford
求单源点到其他点的最短距离,并判断是否有负环。
bool bellman_ford()
{
memset(dis,0,sizeof(dis));
dis[st]=0;
bool rel=0;
for (int i=1;i<=n;i++)
{
rel=0;
for (int j=1;j<=sumn;j++)
if (dis[a[j].x]+a[j].v<dis[aij].v)
{
dis[a[i].y]=a[j].v+dis[a[j].x];
rel=1;
}
if (!rel)
return 0;
}
return 1;
}
4.SPFA
使用邻接表,优化bellman_ford,节约时间和空间。
void SPFA()
{
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
vis[s]=1;
que[1]=s;
head=0;
tail=1;
while (head<tail)
{
int tn=q[++head];
vis[tn]=0;
int te=link[tn];
for (int i=te;i;i=e[i].next)
{
int tmp=e[i].y;
if (dis[tn]+e[i].v<dis[tmp])
{
dis[tmp]=dis[tn]+e[i].v;
if (!vis[tmp])
{
q[++tail]=tmp;
vis[tmp]=1;
}
}
}
}
return ;
}
函数、公式
lower_bound( ) 函数返回大于等于x
upper_bound( )函数返回大于x