单调栈、单调队列与并查集

前言:本篇用了我比较长的时间,干货较多,甚至一篇顶三篇,希望各位耐心观看。

Part 1:单调栈

  单调栈是一种数据结构,一般分为单调递增栈和单调递减栈。单调栈一般用来解决寻找下一个大于或小于m的值。

  我们把得到的答案放到K数组里。

  一个经典例题:定义函数 f(i)代表数列中第i个元素之后第一个大于ai的元素的下标。

  在单调栈中,如果栈为空,那么K[i]就为0,如果栈不为空,则判断栈顶是否大于a[i],如果不是,那么弹出栈顶。最后,不管什么情况,都要把a[i]入栈。需要注意的是,由于中间的中转是栈,所以i从n开始,到1结束。

  单调栈核心代码:

  while(!s.empty()&&a[s.top()]<=a[i])s.pop();

  res[i]=s.empty()?0:s.top();

  s.push(i);

  仅此三行。

  模板题

Part 2:单调队列

  同样,队列内的元素是单调递增或单调递减的,滑动窗口是一道很好的模板题。

  单调队列的核心有两个部分,三个步骤。

  第一个部分(第一步)是队头出队,不管什么情况,队头都要出队,及head++

  第二部分,第二步是判断,如果新元素小于队尾元素,那就直接tail++,也就是直接入队,因为它有可能成为最大值;如果新元素大于等于队尾元素,那么先tail--删除队尾元素,再tail++加入新元素,因为队尾元素一定不可能成为新的最大值。

  第三步就是按照第二步判断出来的执行。

  核心代码同样只有3行:

   if(h<=t&&q[h]+m<=i) h++;
  while(h<=t&&a[i]>=a[q[t]]) t--;
  q[++t]=i;
 当然,有的题非让你用单调递减队列,相反即可。

Part 3 并查集:
  并查集算是比较高级的数据结构了,它主要用来合并序列和查询序列,查询这一方面,其他的算法层出不穷,但合并这一方面,并查集的优势十分显著。
  先说一下基本的定理,并查集是通过找寻/更改“祖先”来进行
合并和查询的,所谓“祖先”其实就是这个数字存在于哪一个集合,一开始要进行初始化,每一个数都以自己的节点号命名一个有1个点的集合,如1号点在1集合1,114514号点就在集合114514。
  合并操作只需要更改公共的祖先即可,比如,一开始,
1号点在1集合1,114514号点在集合114514,如果想把集合114514和集合1合并,那么只需要把114514号点的祖先改为1即可。对于多个点的集合,只要更改其中一个集合中的祖先即可,这里的祖先,指的是点编号与集合编号相同的点,因为它是初始状态,所以是“祖先”。
  那么既然
祖先指的是点编号与集合编号相同的点,那么查找操作就是找某个点的祖先,看他是不是原始祖先(即编号与集合编号相同的点),如果不是,再搜这个点的祖先的祖先,直到找到原始祖先为止。
  可以用递归来写:

    int find(int k) {
      if(f[k]==k) return k;
      return f[k]=find(f[k]);
    }

 
posted @ 2022-07-11 22:28  唯私の超电磁砲  阅读(48)  评论(0编辑  收藏  举报