常见存储、查找算法以及运用

存储(初赛)

  1. 散列存储:即哈希的存储方式。

  2. 顺序存储:数组的存储方式

  3. 链式存储:链式前向星、vector<>

  4. 压缩存储

  5. 索引存储

查找(初赛)

常见查找算法

顺序查找

一个一个往下找,复杂度 \(O(\dfrac{n+1}{2})\)

适合顺序存储,不适合压缩存储、索引存储等。

折半查找、二分查找

查找次数:\(a<\log_2 n<b(a,b,c\in \mathbb{Z})\)

查找失败时,至少比较 \(a\) 次关键字;查找成功时,最多比较关键字次数是 \(b\)

差值查找

咕咕咕

斐波那契查找

咕咕咕

二叉查找树

二叉查找树(英语:Binary Search Tree),也称为二叉搜索树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:

  1. 若任意节点的左子树不空,则左子树上所有节点的key值均小于它的根节点的值;

  2. 若任意节点的右子树不空,则右子树上所有节点的key值均大于它的根节点的值;

  3. 任意节点的左、右子树也分别为二叉查找树;

  4. 没有键值相等的节点。

分块查找

分块查找又称索引顺序查找,它是顺序查找的一种改进方法。

算法的思想是将 \(n\) 个数据元素"按块有序"划分为 \(m\) 块( \(m\le n\) )。每一块中的结点不必有序,但块与块之间必须"按块有序",每个块内的的最大元素小于下一块所有元素的任意一个值。

在块与块之间进行二分操作。

时间复杂度:\(O(\log m + \dfrac{n}{m})\)

哈希查找

哈希通过散列表来实现存储与查找。

其中散列函数只要有一下几类:

  1. 直接定址法:取关键字或关键字的某个线性函数值为散列地址。即 \(\text{Hash}(k) = k\)\(\text{Hash}(k) = a \times k + b\) ,其中 \(a,b\) 为常数(这种散列函数叫做自身函数)

  2. 数字分析法:假设关键字是以 \(r\) 为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。

  3. 平方取中法:取关键字平方后的中间几位为哈希地址。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定。

  4. 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。

  5. 随机数法

  6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 \(\text{Hash}(k) = k \pmod{p}\)\(p\le m\) 。不仅可以对关键字直接取模,也可在折叠法、平方取中法等运算之后取模。对 \(p\) 的选择很重要,一般取素数或 \(m\) ,若 \(p\) 选择不好,容易产生冲突。

为了避免哈希冲突,一般有一下几种方法:

  • 开放寻址法

    • 线性探测法:若发现位置已经被占用,则一个一个往后找。

    • 二次探测法:采用开放定址法处理冲突中的二次探测再散列(也即是题目中的二元探测法),则哈希函数变为 \(\text{Hash}(k)=(\text{Hash}(k)+d) \pmod{mod}\),其中 \(d=1^2,-1^2,2^2,-2^2,3^2,\dots\)

    • 双重散列

  • 链地址法:把数值加入哈希的链表中。

跳跃表

漫画算法:什么是跳跃表?

由于链表二分,所以考虑提取出一些元素比较,从上往下比较,避免了 \(O(n)\) 的比较。

随着元素的加入,需要加入一些的元素作为高一级的链表,一般提高的概率是 \(50\%\)

哈希表

经典例题:P3298 [SDOI2013]泉P5056 【模板】插头dp

哈希表就是一个快速查询一个数是否在一个链表中的方法。

当我们需要记录每一个状态的信息,而状态范围非常大而却很稀疏的时候,就可以用上哈希表啦!

我们对所有状态对一个数取模,对 \([0,mod)\) 内每一个数建立一个链表(链式前向星),用挂链表法存储状态。

当询问一个状态时,就在其对应取模后的链表中寻找,这样可以大大减小查询的复杂度。

即:用空间换时间。

下面代码就是一个查找成功返回 \(val\) 而失败就将 \(val\) 放入的代码:

void Insert(int x,ll v)
{
	 int tmp=x%mod;
	 for(int i=hea[tmp];i;i=nex[i])
	 	 if(sta[i]==x) { return val[i]; }
		 // 查找成功 
	 sta[++tot]=x,nex[tot]=hea[tmp],hea[tmp]=tot,val[tot]=v;
	 // 查找失败,加入哈希表 
}

异或哈希

如果要统计每个数是否出现过奇数次(或出现过正好 \(x\) 次),又不想使用 bitset,则可以给每个数赋上一个初始值 rnd()(笑),那么加入一个数就在计数器上加上这个数对应的随机值,减去这个数就减掉,最终如果计数器上的值等于上面的 \(x\times sum\),则说明正好出现 \(x\) 次(奇数次就异或)。

CF1418G Three Occurrences:用上面的技巧加上一个双指针(扫描线)即可。

CF1746F Kazaee:用上面的技巧,查看在区间内的数的和是否为 \(k\) 的倍数。但是由于只进行一次正确率太低,不妨一直跑知道,去答案的交,快 T 的时候输出。

CF799F Beautiful fountains rows:出现 \(0\) 次与出现奇数次难以同时满足,不妨将 [l,r] 内加一改为 \([l,r-1]\) 内加一。如果区间 \([l,r-1]\) 内的和为偶数,则满足条件。但是我们需要保证所有 \(n\) 列都满足 \([l,r-1]\) 内为偶数,异或哈希!用两个 map 记录异或值为 \(x\) 的点,扫描线过去

posted @ 2021-07-30 16:25  EricQian06  阅读(157)  评论(0编辑  收藏  举报