zoukankan      html  css  js  c++  java
  • 简单数据结构——树状数组、线段树、哈希表

    1.树状数组

    ​ 现阶段,树状数组主要用于维护序列前缀和 (其实还支持区间和、区间异或和、区间乘积和RMQ等具有交换律的问题) 。
    长这样:

    树状数组看似复杂的构造,其实都是以一个思想为基础:

    (i)二进制分解,最小的二的次幂记为lowbit(i)

    结合差分思想,做到区间修改,单点查询。洛谷P3368

    *树状数组求逆序对(二维偏序)洛谷P1908  洛谷P1774

    //定义数组,b[]为读入的数组,a[]为要离散化后的数组,c[]为树状数组
    struct node
    {int v,id;}a[N];
    bool cmp(const node &x,const node &y)
    {return x.v<y.v;}
    int main()
    {
    	scanf("%lld",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%lld",&a[i].v),a[i].id=i;
    	sort(a+1,a+n+1,cmp);//按价值从大到小排序
    	int cnt=1;
    	for(int i=1;i<=n;i++)//离散化+去重
    	{
    		if(i!=1 &&a[i].v!=a[i-1].v)cnt++;
    		b[a[i].id]=cnt;
    	}
    	for(int i=1;i<=n;i++)
    		update(b[i],1),ans=ans+i-sum(b[i]);//因为是排完序之后,所以之前加入的一定比后加入的大
    										//然后在查询当前这个数前面位置的数,就是逆序对的个数了
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    总结:

    时间复杂度:(O(nlogn))(上限) 空间复杂度:(O(n))

    树状数组以其代码量少,思想简洁著称,可以单独使用,也可以和其他数据结构结合,达到锦上添花的效果。不足之处呢,就是能维护的的东西局限较大,难以单独实现其他算法。

    练习洛谷P1774(结合冒泡排序思想就变成逆序对裸题)

    ​  *洛谷P3810(三位偏序,要用到CDQ分治) 还没弄懂

    2.线段树

    ​ 线段树之所以称为“树”,是因为其具有树的结构特性。线段树由于本身是专门用来处理区间问题的(包括RMQ、RSQ问题等。)
    大概长这样:

    几点基础性质:

    1.线段树每个节点都是一个区间

    2.对于每一个区间为([l,r])非叶节点(k),它的左儿子是(k<<1),区间为([l,mid]),右儿子是(k<<1|1),区间为([mid+1,r])。其中(mid=(l+r)>>1)

    下面以维护区间和,支持区间修改的线段树为例,实现它的各种操作。

    关于区间修改操作:

    延迟标记(Lazy tag)

    详细地说,我们在执行修改命令时,可以先不对所有子节点立刻修改,而是在受影响的父节点上标记,表示“该节点已被修改,但其子节点尚未更新”。这样在询问向下递归时,就可以顺便计算父节点标记(修改)对它的影响。

    我们以add数组代表标记数组,每次如果查询子节点就将标记下传

    洛谷P3372

    void pushdown(int k,int l,int r)//标记下传
    {
        if(add[k]==0) return;
        int mid=(l+r)>>1;
        Add(k<<1,l,mid,add[k]);
        Add(k<<1|1,mid+1,r,add[k]);
        add[k]=0;
    }
    

    *标记一个以上,记得考虑运算优先级!洛谷P3373

    其他的题目也差不多,分析好维护的是什么,怎么修改,怎么设置标记,怎么下传。注意细节,线段树的初级应用还是不难的。

    例题:

    洛谷P2574(维护xor)         洛谷SP7259双倍经验的紫题

    总结:

    时间复杂度:(O(nlogn)) 空间复杂度:(O(4n)^+)

    线段树可扩展的空间很大,但是我还没学到那么多,在此就不多赘述了。总而言之,线段树真的是一个非常强的数据结构,在区间维护问题上几乎是万能的存在。我还是慢慢研究吧。

    3.散列表(Hash)

    ​ 与离散化类似的是,Hash能把若干复杂的信息映射到一个容易维护的值域内,从而降低统计难度。哈希的过程,其实可以看作对一个串的单向加密过程,并且需要保证所加的密不能高概率重复

    ​ Hash算法应用较广泛,我只学习了 字符串哈希

    直接上例题理解吧:洛谷P3370

    ​ 给定N个字符串(第i个字符串长度为Mi,字符串内包含数字、大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串。

    对于100%的数据:N<=10000,Mi≈1000。

    进制哈希

    给出出一个固定进制base,将一个串的每一个元素看做一个进制位上的数字,所以这个串就可以看做一个base进制的数,那么这个数就是这个串的哈希值;则我们通过比对每个串的的哈希值,即可判断两个串是否相同。

    long long hashe(char s[])
    {
        int len=strlen(s);
        long long ans=0;
        for (int i=0;i<len;i++)
        ans=(ans*base+s[i])%mod+prime;   //核心操作,可类比十进制。
        								//+prime可增加hash的可行度(不加本题可能会被卡哦)
        return ans;						//mod 最好取一个较大的质数,作用同上。
    }
    

    如果字符串较多,难免出现Hash值一样的字符串,这种现象叫做哈希碰撞

    怎么解决?

    1、无错哈希:挂链表之类的操作。

    2、多重哈希:就是字面意思

    总结:

    Hash表与其说是一种数据结构,不如说是一种sao操作算法。它对于复杂信息的统计有着很大的应用。但是考试时其实Hash的应用不算特别多(我反正基本没用过),而且哈希函数的选择也比较玄学,因此这里就不详细讲了。

    Desperados no way back.
  • 相关阅读:
    网站负载均衡判断
    端口扫描nmap+masscan
    Ant Design Upload 组件之阻止文件默认上传
    Hybrid App技术解析
    react 路由
    webpack进阶(二)
    webpack进阶(一)
    webpack基础
    Promise原理及实现
    TS——类
  • 原文地址:https://www.cnblogs.com/Zerosking/p/10011076.html
Copyright © 2011-2022 走看看