单链表
定义头节点head;节点数组e[N];节点指针数组ne[N];节点下标idx
前一个节点的指针=下一个节点的节点下标
初始化(head = -1;idx=0)
注意下标,第1个插入的是e[0]; 第k个插入的后面删除删除的是k-1;同时当k=0删除头节点,即将指针移动head=ne[head]
双链表
不再定义头节点,默认0为头节点,1为尾节点;同时有两个指针,分别指向前一个节点和后一个节点;
栈
//定义栈,栈底下标
int stk[N],tt;
//插入
stk[++tt]=x;
//弹出
tt--;
//判断是否为空
if(tt>0) not empty
else empty
//栈顶
stk[tt];
队列
//队列,队头,队尾
int q[N],hh=0,tt=-1;
//队尾插入,
q[++tt]=x;//队头弹出
hh++;
if(hh>tt) empty
else not empty
//取出队头,队尾元素
q[hh];q[tt];
单调栈
定义: 单调栈就是栈内元素递增或者单调递减的栈,并且只能在栈顶操作。单调栈的维护是O(n)的时间复杂度,所有元素只会进进栈一次
性质:
- 单调栈里面的元素具有单调性;
- 元素加入栈前会把栈顶破坏单调性的元素删除;
- 使用单调栈可以找到元素向左遍历的第一个比他小的元素(单增栈),也可以找到元素向左遍历第一个比他大的元素(单减栈);
- 一般使用单调栈的题目具有以下的两点:
- 离自己最近(栈的后进先出的性质)
- 比自己大(小)、高(低);
- 单调栈可以找到元素向左遍历第一个比他小(大)的元素,也就是说在元素进栈前他向左拓展的区间已经确定,在出栈前她能向右拓展的区间也能确定(左区间好理解,仔细体会右区间的确定,若该元素至遍历结束后也未出栈,那么就是说在原数组中,该元素的右方向没有一个元素可以比它大/小,那么该元素的右边界就是原数组的大小(就是没有右边界),否则它的右边界就是令它出栈的元素)。
单调队列
利用单调队列的方式能把该题的复杂度将为O(N)
具体思路: 设置一个双端队列来维护窗口内数据最多纪录窗口大小K个元素 ,由于采用单调队列,所以队列中的元素是严格按照元素大小递减的方式,为了维护窗口的范围,即窗口以外的元素要及时丢掉 ,队列中保存的并不是元素本身而是元素在原数组当中的序号或则索引。
算法可描述为:
- 设置头结点hh,尾结点tt,队列q[N] ——存储原数据在原数组的索引 a[N]存储索引值
- 判断头结点对应的索引是否处于i-k+1内部,如果不是,移动头结点
- 如果第i个元素对应值大于栈里面元素的值,则删除原有栈里面的元素
- 把第i个元素加入栈中,即:q[+tt]=i;注意此时tt<i;
- 输出a[q[hh]]的值
KMP算法:
简介:Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP算法”,常用于在一个文本串S内查找一个模式串P 的出现位置。
暴力解法:
- 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
- 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。
主要思想:当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值
next 数组:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。即:在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)
next[i] 首先j去尝试,如果不行的话,退到ne[j]的位置,
Tire树
Tire高效的存储和查找字符串集合的数据结构。
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
trie树每一层的节点数是26^i级别的。所以为了节省空间。我们用动态链表,或者用数组来模拟动态。空间的花费,不会超过单词数×单词长度。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有3个基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
已知n个由小写字母构成的平均长度为10的单词,判断其中是否存在某个串为另一个串的前缀子串。下面对比3种方法:
- 最容易想到的:即从字符串集中从头往后搜,看每个字符串是否为字符串集中某个字符串的前缀,复杂度为O(n^2)。
- 使用hash:我们用hash存下所有字符串的所有的前缀子串,建立存有子串hash的复杂度为O(n*len),而查询的复杂度为O(n)* O(1)= O(n)
- 使用trie:因为当查询如字符串abc是否为某个字符串的前缀时,显然以b,c,d....等不是以a开头的字符串就不用查找了。所以建立trie的复杂度为O(n*len),而建立+查询在trie中是可以同时执行的,建立的过程也就可以成为查询的过程,hash就不能实现这个功能。所以总的复杂度为O(n*len),实际查询的复杂度也只是O(len)。(说白了,就是Trie树的平均高度h为len,所以Trie树的查询复杂度为O(h)=O(len)。好比一棵二叉平衡树的高度为logN,则其查询,插入的平均时间复杂度亦为O(logN))。
参考:https://blog.csdn.net/bjzhaoxiao/article/details/80481693
并查集
p[a]=b a号点的父节点是b号点
并查集的最主要思想是找到集合的根节点;同时可以进行路径压缩,让每一个节点都指向根节点,这一步在根节点查找的函数里面实现即可;
堆
哈希表
哈希表:哈希算法是一种期望算法
一般在哈希表中没有删除操作,只有添加和查找的操作;如果非要进行删除的操作,一般只是在变量旁边开一个布尔量进行标 记。
存储结构
冲突的处理方式,离散化是一种极其特殊的哈希方式
开放寻址法
拉链法:开一个一维数组来存储哈希值 ,每一条链的长度可以看成是常数,一般哈希表的时间复杂度可以看成是o(1)。
拉链法用到的是单数组+数组模拟邻接表的写法。
哈希函数
常用字符串的哈希方式
尽量不要用scanf读取字符,因为它会自动忽略空格......等字符
哈希的精髓:以一个K进制的角度来将一个字符串看成一个数字