今天讲数据结构

先从mzx大佬的ppt摘抄一段:

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。

通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。(摘自百度百科)

简单来说,我们可以把数据的集合视为一个数组,即数组的元素就是我们所存储的数据,

同时,我们再用一系列指针(在OI中一般不用指针而用下标来代替)维护这些数据之间的关系,使得我们能够更方便地管理我们所需要用到的数据。

数据结构很多时候是用来优化算法的,所以我们需要搞清楚我们算法需要对数据进行什么样的操作,根据需求来选择所需要的数据结构。

在学习一个数据结构时,你应该按这样的先后顺序来记忆: 1.作用(支持的操作)及复杂度 2.原理(代码实现) 3.正确性/复杂度证明

数据结构存储的是数据,它们可以是整数(int/long long),实数(float/double),也可以是我们自己定义的结构体。

目录:

队列

堆(优先队列)

分块

线段树

树状数组

一.栈

栈是一种先进后出(FILO,即first-in-last-out)的数据结构,你可以把它想象成一个只开一个口的乒乓球筒,每次只能从最顶端处放置或取出数据。

一般在OI中我们用一个数组和栈顶指针(用int表示的下标)来模拟一个栈。

栈支持两个操作:1.向栈顶加入一个元素 2.从栈顶取出一个元素 这两个操作的时间复杂度都是O(1)。

那么它所维护的这个数据集合中,数据之间有什么样的关系呢?

很简单,就是fisrt-in-last-out,或者更具体一点:如果一个元素在另一个元素之前加入这个集合中,那么它一定在那个元素之后退出这个集合。(摘自mzx大佬的ppt)

看一道例题:

火车进站

有一个车站,每天都会有N辆车进站,进站按从1到N的顺序进站。

现在车站的站长想让这些火车按照特定的顺序出站,问可以做到吗?

当N为5时,出站顺序若为1 2 3 4 5,可以做到,但是顺序若为5 4 1 2 3,则不行。

直接用栈模拟:

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. int n,a[],s[],now,top;
  5. int main()
  6. {
  7. scanf("%d",&n);
  8. for(int i=;i<=n;i++)
  9. scanf("%d",&a[i]);
  10. now=;
  11. for(int i=;i<=n;i++)
  12. {
  13. top++;
  14. s[top]=i;
  15. while((s[top]==a[now])&&(top>)&&(now>))
  16. {
  17. top--;
  18. now++;
  19. }
  20. }
  21. if(top!=)
  22. printf("NO");
  23. else
  24. printf("YES");
  25. return ;
  26. }

值得注意的是此题时间复杂度需要用均摊分析:

第二层循环一共执行n次,最坏一次n次,均摊后复杂度为O(1),总复杂度为O(n)。by——cym大佬的笔记(其实还是mzx讲的)

单调栈:

单调栈只是栈的一个特殊用法而已,本质上依然是一个栈。

单调栈要求栈中的元素从栈底到栈顶是单调(有序)的,所以在加入一个新的元素时,如果它和栈顶元素不满足单调,那么将会依次弹出栈顶的元素直到这个元素加入到栈中依然满足单调,才

将这个元素加入到栈中。

队列:

队列是一种先进先出(FIFO,即first-in-fisrt-out)的数据结构,你可以把它想象成一个羽毛球筒,每次只能从尾部添加元素,从头部取出元素,除非你想把你的羽毛球弄坏。

一般在OI中我们用一个数组和队头和队尾指针(用int表示的下标)来模拟一个队列。

队列支持两个操作:1.向队尾加入一个元素 2.从队头取出一个元素 这两个操作的时间复杂度都是O(1)。

那么它所维护的这个数据集合中,数据之间有什么样的关系呢? 很简单,就是fisrt-in-first-out,或者更具体一点:如果一个元素在另一个元素之前加入这个集合中,那么它一定在那个元素之前

退出这个集合。

上面两个没例题是因为我懒...有时间会补的(胡说八道)

其实我更关心的是这个:

单调队列!

单调队列只是队列的一个特殊用法而已,本质上依然是一个队列。

单调队列要求队列中的元素从队头到队尾是单调(有序)的,所以在加入一个新的元素时,如果它和队尾元素不满足单调,那么将会依次弹出栈顶的元素直到这个元素加入到队尾依然满足单

调,才将这个元素加入到队列中。

需要注意的是,这个插入到队尾的操作和单调栈比较相似,甚至可以说一样,不同的是取元素时的位置。

例题:

首先想到的就应该是:滑动窗口!

题目链接:https://www.luogu.org/problemnew/show/P1886

这道题是一道典型的单调队列

在这里引用luogu一位大佬hankeke对单调队列的讲解:

单调队列有两个性质

  1. 队列中的元素其对应在原来的列表中的顺序必须是单调递增的。

  2. 队列中元素的大小必须是单调递*(增/减/甚至是自定义也可以)

单调队列与普通队列不一样的地方就在于单调队列既可以从队首出队,也可以从队尾出队。

那么我们应该怎样实现单调队列呢?

就拿样例来谈谈,设以最小的为标准。

  1. 8 3
  2. 1 3 -1 -3 5 3 6 7

下文中我们用q来表示单调队列,p来表示其所对应的在原列表里的序号。

  1. 由于此时队中没有一个元素,我们直接令1进队。此时,q={1},p={1}。

  2. 现在3面临着抉择。下面基于这样一个思想:假如把3放进去,如果后面2个数都比它大,那么3在其有生之年就有可能成为最小的。此时,q={1,3},p={1,2}

  3. 下面出现了-1。队尾元素3比-1大,那么意味着只要-1进队,那么3在其有生之年必定成为不了最小值,原因很明显:因为当下面3被框起来,那么-1也一定被框起来,所以3永远不能当最小值。所以,3从队尾出队。同理,1从队尾出队。最后-1进队,此时q={-1},p={3}

  4. 出现-3,同上面分析,-1>-3,-1从队尾出队,-3从队尾进队。q={-3},p={4}。

  5. 出现5,因为5>-3,同第二条分析,5在有生之年还是有希望的,所以5进队。此时,q={-3,5},p={4,5}

  6. 出现3。3先与队尾的5比较,3<5,按照第3条的分析,5从队尾出队。3再与-3比较,同第二条分析,3进队。此时,q={-3,3},p={4,6}

  7. 出现6。6与3比较,因为3<6,所以3不必出队。由于3以前元素都<3,所以不必再比较,6进队。因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时,q={3,6},p={6,7}

  8. 出现7。队尾元素6小于7,7进队。此时,q={3,6,7},p={6,7,8}。

那么,我们对单调队列的基本操作已经分析完毕。因为单调队列中元素大小单调递*(增/减/自定义比较),因此,队首元素必定是最值。按题意输出即可。

代码如下:

  1. #include <iostream>
  2. #include <cstdio>
  3. using namespace std;
  4. const int maxn = + ;
  5. int a[maxn], que[maxn], ppp[maxn], head, tail, k, n;
  6. int min_queue()
  7. {
  8. head = ; tail = ;
  9. for(int i = ; i <= n-; i++)
  10. {
  11.  
  12. while(head<=tail && que[tail]>=a[i]) tail--;
  13. que[++tail] = a[i];
  14. ppp[tail] = i;
  15. while(ppp[head]<=i-k)
  16. head++;
  17. if(i>=k) printf("%d\n",que[head]);
  18. }
  19. printf("\n");
  20. }
  21. int max_queue()
  22. {
  23. head = ; tail = ;
  24. for(int i = ; i <= n; i++)
  25. {
  26. while(head<=tail && que[tail]<=a[i]) tail--;
  27. que[++tail] = a[i];
  28. ppp[tail] = i;
  29. while(ppp[head]<=i-k)
  30. head++;
  31. if(i>=k) printf("%d ",que[head]);
  32. }
  33. }
  34. int main()
  35. {
  36. scanf("%d%d", &n, &k);
  37. for(int i = ; i <= n; i++)
  38. scanf("%d",&a[i]);
  39. //printf("0\n");
  40. min_queue();
  41. max_queue();
  42. return ;
  43. }

还有一道是luogu的P1440

这道题看到是让查询区间的最值,果断想到用ST表头铁一波,然后T了两个点(当然有的大佬会用ST表优化过掉...然而我不会...)

  1. //ST表头铁80分
  2. #include <cstdio>
  3. #include <algorithm>
  4. using namespace std;
  5. const int maxn = + ;
  6. int st[maxn][], ans[maxn];
  7. int j, i, left, right, n, m, a;
  8. int main()
  9. {
  10. scanf("%d%d",&n, &m);
  11. for(i = ; i <= n; i++)
  12. {
  13. scanf("%d",&a);
  14. st[i][] = a;
  15. }
  16.  
  17. for(j = ; (<<j) <= n; j++)
  18. for(i = ; i <= n-(<<j)+; i++)
  19. st[i][j] = min(st[i][j-],st[i+(<<(j-))][j-]);
  20.  
  21. for(i = ; i <= n-m+; i++)
  22. {
  23. j = ;
  24. left = i;
  25. right = left + m - ;
  26.  
  27. while((<<(j+)) <= (right-left+)) j++;
  28. ans[i] = min(st[left][j],st[right-(<<j)+][j]);
  29.  
  30. }
  31. printf("0\n");
  32. for(int i=;i<=m-;i++)
  33. {
  34. left = ;
  35. right = i;
  36. j = ;
  37. while((<<(j+)) <= (right-left+)) j++;
  38. printf("%d\n",min(st[left][j],st[right-(<<j)+][j]));
  39. }
  40. for(i = ; i <= n-m; i++)
  41. printf("%d\n",ans[i]);
  42. }
  43. //前m-1个数 ,第一个一定是0

当然正解就是我们要用到的单调队列啦

其实把滑动窗口的改一改就好...

  1. #include <iostream>
  2. #include <cstdio>
  3. using namespace std;
  4. const int maxn = + ;
  5. int a[maxn], que[maxn], ppp[maxn], head, tail, k, n;
  6. int min_queue()
  7. {
  8. head = ; tail = ;
  9. for(int i = ; i <= n-; i++)
  10. {
  11.  
  12. while(head<=tail && que[tail]>=a[i]) tail--;
  13. que[++tail] = a[i];
  14. ppp[tail] = i;
  15. while(ppp[head]<=i-k)
  16. head++;
  17. /*if(i>=k)*/ printf("%d\n",que[head]);
  18. }
  19. printf("\n");
  20. }
  21. /*int max_queue()
  22. {
  23. head = 1; tail = 0;
  24. for(int i = 1; i <= n; i++)
  25. {
  26. while(head<=tail && que[tail]<=a[i]) tail--;
  27. que[++tail] = a[i];
  28. ppp[tail] = i;
  29. while(ppp[head]<=i-k)
  30. head++;
  31. if(i>=k) printf("%d ",que[head]);
  32. }
  33. }*/
  34. int main()
  35. {
  36. scanf("%d%d", &n, &k);
  37. for(int i = ; i <= n; i++)
  38. scanf("%d",&a[i]);
  39. printf("0\n");
  40. min_queue();
  41. max_queue();
  42. return ;
  43. }

优先队列

“优先队列”这个概念包括了很多数据结构,我们把具有以下功能的数据结构统称为“优先队列”,或者说这些数据结构是“优先队列”的不同实现方式:

1.将一个元素插入到集合中

2.取出集合中最小/最大的元素 支持以上操作的数据结构有二叉堆/斜堆/二项堆/斐波那契堆等,这两个操作的时间复杂度一般为O(logn)(n为集合大小),当然,它们同时也支持很多其他操作,

比如修改元素的大小或者合并两个堆(支持这个操作的堆也叫可并堆)等,通过名字可以看到,优先队列一般也被称为“堆”。

但是,对于C++选手来说,stl内置了一个叫priority_queue(翻译即为优先队列)的数据结构,它支持插入,查询或删除最大值的操作,所以,我们一般只需要直接使用它而不需要自己手动实现

一个二叉堆(除非你需要支持其他的操作)。

在本博客里有STL堆模板了,这里不再放。

例题:合并果子

题目链接:https://www.luogu.org/problemnew/show/P1090#sub

思路:每次取出最小的两堆,合并完再入堆,直到堆里面没有元素。

学大伟业 Day 5 培训总结的更多相关文章

  1. 学大伟业 Day 1 培训总结

    第一天培训,讲的基本算法,东西很多.还有些数论,图论,数据结构and some small tricks 一.输入输出技巧 //输入输出技巧 /* scanf.printf:速度快,需要记忆不同数据类 ...

  2. 学大伟业 Day 6 培训总结

    今天接着昨天的继续讲数据结构 今天先是 分块 在统计问题中,尤其是序列问题,经常涉及到区间的操作,比如修改一段区间的元素,询问某个区间的元素的信息. 如果每次都对一整个区间的每一个元素进行操作的话,那 ...

  3. 学大伟业 Day 3 培训总结

    今天讲的字符串: 不多说,直接看题 一.表达式求值 题目大意: 输入一行一个表达式,计算其答案 表达式包含非负整数.加减乘除.括号 两种做法 ·栈 ·表达式树 这里更推荐表达式树,因为栈是先压进去,逆 ...

  4. 学大伟业 Day 2 培训总结

    一.dp 动态规划的本质 是一种思想.通过对原问题划分成子问题,寻找子问题之间的联系,通过求解子问题得出原问题的解.与贪心不同的是,动归是深谋远虑,考虑全局最优解:而贪心则目光短浅,只考虑局部最优解. ...

  5. 学大伟业 Day 4 培训总结

    今天讲的全是dp... 不多废话,先看一道经典的模板LIS(最长不下降子序列) 一.LIS 给定一个长度为N的数列,求最长上升子序列 例:1 7 2 8 3 4 答案:1 2 3 4 代码: #inc ...

  6. 学大伟业Day1解题报告

    学大伟业Day1解题报告 张炳琪 一.   时间分配 T1:30分钟  T2: 60分钟  T3:100分钟 二.答题情况及错因 T1:100         T2:55             T3 ...

  7. 学大伟业 2017 国庆 Day1

    期望得分:100+100+20=220 实际得分:100+100+20=220 (好久没有期望==实际了 ,~\(≧▽≦)/~) 对于 a........a 如果 第1个a 后面出现的第1个b~z 是 ...

  8. 2017-10-23学大伟业Day1

    T1 叉叉 题目名称 叉叉 程序文件名 cross 输入文件名 cross.in 输出文件名 cross.out 每个测试点时限 1秒 内存限制 128MB 测试点数目 10 每个测试点分值 10 是 ...

  9. 学大伟业 国庆Day2

    期望得分:30+100+0=130 实际得分:30+100+20=150 忍者钩爪 (ninja.pas/c/cpp) [问题描述] 小Q是一名酷爱钩爪的忍者,最喜欢飞檐走壁的感觉,有一天小Q发现一个 ...

随机推荐

  1. [转]微信小程序(应用号)是什么,是否值得投入进来做?

    本文转自:http://www.woshipm.com/it/417887.html 距离张小龙的那场首次公开演讲已经有九个月了,而在那场演讲中备受关注的「应用号」在千呼万唤中终于以「小程序」的名字正 ...

  2. 解决The current branch is not configured for pull No value for key branch.master.merge found in config

    使用Git Pull项目的时候出现这个问题: The current branch is not configured for pull No value for key branch.master. ...

  3. opencv2.4.10+VS2012配置问题

    opencv2.4.10+VS2012配置 作为opencv的初学者,第一个难题想必都一样,如何配置opencv+VS的环境呢?在网上的教程,铺天盖地,但我仍然是尝试了十几次才找到属于自己的那套配置方 ...

  4. Vue2.0以后,有哪些变化

    最近移动端项目版本升级,Vue由之前的1.0升级到2.3,那么,Vue2.0之后,有哪些细节的变化呢,现在总结如下: 1.在每个组件模板,不再支持片段代码 组件中模板: 之前: <templat ...

  5. 《Java开发实战经典》读书笔记

    Java常用的内存区域: (1)      栈内存空间:保存所有的对象名称. (2)      堆内存空间:保存每个对象的具体属性内容. (3)      全局数据区:保存static类型的属性. ( ...

  6. 简单Java程序向实用程序的过度:二进制文件的读写

    File I/O中常见的文件读写: 1.字节流读写文本文件 FileInputStream; FileOutputStream; 2.字符流读写文本文件 FileReader; FileWriter; ...

  7. win10x系统下的Git下载安装

    git安装和使用百度一下就有,官方地址https://git-scm.com/book/zh/v1/起步-安装-Git 但是说的并不是很详细,自己记录一下, 首先我们去官网下载一个git 有两个下载地 ...

  8. 如何查询日志文件中的所有ip,正则表达式

    IPV4必须满足以下四条规则: 1.任何一个1位或2位数字,即0-99: 2.任何一个以1开头的3位数字,即100-199: 3.任何一个以2开头.第2位数字是0-4之间的3位数字,即200-249: ...

  9. hdu 1087 最大递增和

    思路和LIS差不多,dp[i]为i结尾最大值 #include <iostream> #include <string> #include <cstring> #i ...

  10. PAT 1055 The World's Richest

    #include <cstdio> #include <cstdlib> #include <cstring> #include <vector> #i ...