• 数据结构


    队列和栈

    queue<int>q;
    int n;
    
    int main() {
    //  freopen(".in", "r", stdin);
    //  freopen(".out", "w", stdout);
      n=read();
      for (int i=1;i<=n;i++) 
      {
        int opt=read();
        if(opt==1){
          int x=read();
          q.push(x);
        }
        else {
          printf("%d
    ",q.front());
          q.pop();
        }
      } 
      fclose(stdin);
      fclose(stdout);
      return 0;
    }
    
    
    
    int st[B], head=0, tail=0, n;
    
    int main() {
    //  freopen(".in", "r", stdin);
    //  freopen(".out", "w", stdout);
      n=read();
      for (int i=1;i<=n;i++)
      {
        int opt=read();
        if(opt==1)
        {
          int x=read();
          st[++head]=x;
        }
        else{
          printf("%d
    ", st[head]);
          head--;
        }
      }
      fclose(stdin);
      fclose(stdout);
      return 0;
    }
    
    

    单调队列(松弛操作)

    /*暴力O(n^2)*/
    for (int a=1;a<=n-k+1;i++)
    {
    	int ans=oo;
    	for (int b=0;b<k;b++)
    	{
    		ans=min(ans,z[a+b]);
    	}
    	printf("%d
    ",ans);
    }
    

    法1 单调队列

    本质:在前面删一个数,从后面加一个数。队列+最小值

    样例

    1 3 -1 -3 5 3 6 7, 3

    要求最小值,维护单调递增的,

    不单调的本质就是最后的数和他不是递增的,所以扔掉就好了,

    保持单调递增根最小值有啥关系呢?

    单调队列中队首就是我们需要的最小值,维护区间长度就是判断队尾和队首是否存在来维护即可

    int z[B], n;
    
    struct monotone_queue//单调递增
    {
    	int head,tail;
    	int q[233333];
    	monotone_queue()
    	{
    	head=1,tail=0;
    	}
    	void push(int p)
    	{
    		while (head<=tail && z[q[tail]] >= z[p]) tail--;
    		q[++tail]=p;
    	}
    	int front (){
    	 return q[head];
    	}
    	void pop()
    	{
    		head++;
    	}
    }q;
    
    int main()
    {
    	scanf("%d%d",&n,&k);
    	for (int i=1;i<=n;i++)
    	scanf("%d",&z[i]);
    	for (int i=1;i<=k;i++)
    		q.push(i);//放入队列的是坐标
    	printf("%d
    ", z[q.front()]);
    	for (int a=k+1;a<=n;a++)
    	{
    		q.push(a);
    		if (q.front()==a-k) q.pop();
    		printf("%d
    ", z[q.front()]);
    	}
    }
    

    法2 ST表

    (O(nlogn))

    例题

    笔记

    思路

    如果 (l=1) 那么怎么找右 (r);

    int n, k;
    int z[23333];
    
    int l=1,r=1;
    int sum=z[1];
    
    while(sum<k)
    {
    	r++;
    	sum+==z[r];
    }
    ans=min(ans, l-r+1)// 左端点为1时,满足条件区间时多少
    

    那么 (l=2) 时呢,同理

    那么请问,(r1,r2) 之间有什么关系 (r2>r1),这里指的是坐标

    int n, k;
    int z[23333];
    
    int l=1,r=1;
    int sum=z[1];
    
    for (int l=1,r=1,sum=z[l];r>=n;)
    {
        while(r<n && sum<k)
        {
            r++;
            sum+=z[r];
        }
        if(sum>=k) ans=min(ans, r-l+1);
        else break;
        sum-=z[l];
        l++;
    }
    

    复杂度是 (O(n)) 每个元素只会被加入一次和出队一次,而且队首和队尾循环时独立的并不冲突,所以总的复杂度为 (O(n)),

    共同性:当区间左端点右移的时候,右端点也一定右移,

    STL

    双端对列 (deque)

    include <deque>
    
    void push(int p)
    {
    	while (q.size() && z[q.back()] >= z[p])
    		q.pop_back();
    	q.push_back();
    }
    
    int main()
    {
    	scanf("%d%d",&n,&k);
    	for (int i=1;i<=n;i++)
    	scanf("%d",&z[i]);
    	for (int i=1;i<=k;i++)
    		q.push(i);//放入队列的是坐标
    	printf("%d
    ", z[q.front()]);
    	for (int a=k+1;a<=n;a++)
    	{
    		q.push(a);
    		if (q.front()==a-k) q.pop_front();
    		printf("%d
    ", z[q.front()]);
    	}
    }
    

    大根堆

    • 加入一个数
    • 询问最大值
    • 删除最大值

    小根堆

    • 相反即可

    STL

    大根堆-priority_queue<int> head

    小根堆-将数字取个负数(相反数)

    手写堆

    如何构造二叉树

    如何完成堆

    大根堆:任何一个点的值都不小于儿子的值

    只能删掉最大值!

    srtuct heap{
    	int n;//元素个数
    	int z[2333333];//村原属;
    	heap()
    	{
    		n=0;
    	}
    	
    	int size()
    	{
    		return n;
    	}
    	int push()//加入x
    	{
    		n++;
    		z[n]=x;
    		int p=n;
    		while (p!=1)
    		{
    			int f=p/2;
    			if (z[f]<z[p])//父亲节点是p/2
    			{
    				swap(z[f],z[p]);
    	               p=f;//一直换
    			}
    			else break;
    		}
    	}
    	int pop()//删除最大值
    	{
    		swap([z[1],z[n]]);
            n--;//保证树的形态没改变,但是堆的性质没了,所以就需要头不断的向下换
            
            int p=1;
            while (p*2<=n)
            {
                int pp=p*2;//比较两个儿子的大小
                if (pp+1 <= n && z[pp+1] > z[pp]) pp+=1;
                if (z[p] < z[pp])
                {
                    swap(z[p], z[pp]);
                    p=pp;
                }
                else break;
            }
    	}
    	int top()
    	{
    		return z[1];
    	}
    }
    

    树状数组

    (N) 个数 (a_1,a_2...,a_n),

    • (a_i) 加上 (v);
    • 询问 ([l,r]) 区间和

    超级快,常数比较小,解决数据小的,且功能小;

    QQ截图20210218093056

    (lowbit(x)=2^y) 得到数中最多的 (2^n)

    (y_i)(i) 点往下走能走到 (z) 位置的和;

    (y_i=z_i+z_{i-1}...+z_{i-lowbit(i)+1})

    所以询问 ([l,r]) 和就是前缀和,就是 ([1,r]-[1,l-1])

    那么怎么询问 ([1,p]) 和?

    [s(p)=egin{matrix}y(p)\overbrace{a_p+a_{p-1}+a_{p-2}+a_{p-lowbit(p)}}end{matrix} +egin{matrix}y(p-lowbit(p))\overbrace{aa_{p-lowbit(p)+1}...+a_1}end{matrix} ]

    #define lb(x) x&(-x)
    
    int n, y[23333];
    
    
    void modify(int p, int v)
    {
    	for (;p<=n;p+=lb(p)) y[p]+=v;
    }
    
    int query(int p)
    {
    	int ans=0;
    	for (;p-=lb(p);) ans+=y[p];
    	return ans;
    }
    
    int main()
    {
    	scanf("%d",&n);
        //发一预处理
        for (int a=1;a<=n;a+=2)
            	lb[a]=1;
        for (int a=1;a<=n/2;a++)
            	lb[a*2]=lb[a]*2;
    	for (int i=1;i<=n;i++)
    	{
    		int v;
    		scanf("%d",&v);
    		modify(a,v);
    	}
    	scanf("%d",&m);
    	for (int i=1;i<=m;i++)
    	{
    		int p, v;
    		modify(p,v);
    		query(p);
    	}
    }
    

    单点询问,区间修改

    差分的思想就将单点询问转换成区间询问

    例题

    有多少对逆序对?

    思路

    对于一个数 (a_i) 他能产生的逆序对就是比前面小的,比后面大的即可,

    那么先考虑只有前面的

    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    #define lb(x) x&(-x)
    using namespace std;
    
    const int A = 1e5 + 11;
    const int B = 1e8 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    int m, y[B], z[B],n;
    
    void updata(int x,int v){
      for (;x<=n;x+=lb(x)) y[x]+=v;
    }
    
    int query(int x)
    {
      int ans=0;
      for (;x;x-=lb(x)) ans+=y[x];
      return ans;
    }
      
    
    int main() {
    //  freopen(".in", "r", stdin);
    //  freopen(".out", "w", stdout);
      m=read();
      for (int i=1;i<=m;i++)
        scanf("%d",&z[i]), n=max(n,z[i]);
      int ans=0;
      for (int i=1;i<=m;i++)
      {
        updata(z[i],1);
        ans+=query(n)-query(z[i]);
      }
      printf("%d
    ",ans);
      fclose(stdin);
      fclose(stdout);
      return 0;
    }
    
    
    

    problem 14

    1-N的无序排列,做旋转置换,问怎样的置换能够使得得到的逆序对最小

    
    int m, y[B], z[B],n;
    
    void updata(int x,int v){
      for (;x<=n;x+=lb(x)) y[x]+=v;
    }
    
    int query(int x)
    {
      int ans=0;
      for (;x;x-=lb(x)) ans+=y[x];
      return ans;
    }
      
    
    int main() {
    //  freopen(".in", "r", stdin);
    //  freopen(".out", "w", stdout);
      n=read();
      int ans=0;
      for (int i=1;i<=n;i++) updata(i,1);
      for (int i=1;i<=n;i++)
      {
        cin>>z[i];
        updata(z[i]+1,-1);
        ans+=query(z[i]+1);
      }
      int sum=ans;
      for (int i=1;i<=n;i++)
      {
        ans+=(-z[i]+n-z[i]-1);
        if (ans<sum)
          sum=ans;
      }
      cout << sum;
    }
        
    
    
    
  • 相关阅读:
    在网易和百度实习之后,我才明白了这些事
    从Java小白到收获BAT等offer,分享我这两年的经验和感悟
    曾经做的一个JS小游戏——《Battle City》
    适配器(Adapter)模式
    装饰器(Decorator)模式
    Java IO
    JDBC中驱动加载的过程分析
    从PipedInputStream/PipedOutputStream谈起
    从InputStream到ByteArrayInputStream
    JDK中的动态代理
  • 原文地址:https://www.cnblogs.com/lToZvTe/p/14412382.html
Copyright © 2020-2023  润新知