数据结构专题

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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值