链表、栈与队列、kmp;

数组模拟单链表:

用的最多的是邻接表--就是多个单链表:

作用:存储树与图

需要明确相关定义:

为什么需要使用数组模拟链表

  1. 比使用结构体 或者类来说 速度更快
  2. 代码简洁
  3. 算法题:空间换时间

题目详情

图解:

head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点

import java.util.*;

public class Main{
static int N = 100010;
static int idx,head;
static int[] e = new int[N];
static int[] next = new int[N]; // 初始化
static void init(){
head = -1;
idx = 0;
}
// 向链表头插入一个数
static void insertHead(int x){
e[idx] = x;
next[idx] = head;
head = idx++;
} // 向k位置插入x
static void insert(int k,int x){
e[idx] = x;
next[idx] = next[k];
next[k] = idx++;
} // 删除k位置的数
static void delete(int k){
next[k] = next[next[k]];
}
// 主函数
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
init();
int n = sc.nextInt();
while(n-- != 0){
String s = sc.next();
if(s.equals("H")){
int x = sc.nextInt();
insertHead(x);
}else if(s.equals("D")){
int k = sc.nextInt();
if(k == 0) head=next[head];
else delete(k-1);
}else if(s.equals("I")){
int k = sc.nextInt();
int x = sc.nextInt();
insert(k-1,x);
} }
for(int i =head;i != -1;i=next[i]){
System.out.print(e[i]+" ");
} } }

数组模拟双链表:

作用:优化某些问题

题目详情

栈:

先进后出

// tt表示栈顶
int stk[N], tt = 0; // 向栈顶插入一个数
stk[ ++ tt] = x; // 从栈顶弹出一个数
tt -- ; // 栈顶的值
stk[tt]; // 判断栈是否为空
if (tt > 0)
{ }

队列:

先进先出

1. 普通队列:
// hh 表示队头,tt表示队尾
int q[N], hh = 0, tt = -1; // 向队尾插入一个数
q[ ++ tt] = x; // 从队头弹出一个数
hh ++ ; // 队头的值
q[hh]; // 判断队列是否为空
if (hh <= tt)
{ }
2. 循环队列
// hh 表示队头,tt表示队尾的后一个位置
int q[N], hh = 0, tt = 0; // 向队尾插入一个数
q[tt ++ ] = x;
if (tt == N) tt = 0; // 从队头弹出一个数
hh ++ ;
if (hh == N) hh = 0; // 队头的值
q[hh]; // 判断队列是否为空
if (hh != tt)
{ }

单调栈:

题目详情

每个数找到左边离自己最近且比自己小的数:

暴力算法 :

import java.util.*;
import java.io.*; public class Main{
static int N = 100010;
static int[] q = new int[N]; public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i = 0;i < n; i++){
q[i] = sc.nextInt();
} for(int i = 0;i < n;i++){
int index =0;
for(int j = i-1;j >=0;j--){
if(q[i]>q[j]){
index = j;
System.out.print(q[j]+" ");
break;
}
}
if(q[i]<=q[index]){
System.out.print("-1 ");
}
} } }

AC代码:

import java.util.*;

public class Main{
static int N = 100010;
static int[] stk = new int[N];
static int tt=0;
public static void main(String[] args){
Scanner sc = new Scanner(System.in); int n = sc.nextInt(); for(int i = 0; i < n; i++){ int x = sc.nextInt();
// 如果栈中还有元素并且栈顶元素大于x的话,从栈顶弹出;
while(tt != 0 && stk[tt] >= x) tt--;
// 上面结束以后是栈顶元素小于输入的元素; stk[tt] < x; 这样就保证了x元素在左边并且小于本身的元素是栈顶元素;
//如果栈中有元素,输出栈顶元素
if(tt != 0) System.out.print(stk[tt]+" ");
// 否则栈中没有元素
else System.out.print("-1"+" ");
// 将元素加入栈中
stk[++tt] = x;
} } }

单调队列:

滑动窗口:

解题思路:

  1. 判断队头出没出窗口 if true-->hh++;
  2. 求最小值,保持队列单调上升,判断队尾元素tt与当前元素a[i]的大小,若tt >=a[i],剔除队尾元素;
  3. 求最大值,保持队列单调下降,判断队尾元素tt与当前元素a[i]的大小,若tt <=a[i],剔除队尾元素;
  4. 将当前元素下标加入队尾;
  5. 如果满足条件输出

注:队列是先进先出模式,只有在队列中保持单调性 才能保证,队头为最小值或者最大值;

import java.io.*;
public class Main{
static int N = 1000010;
// 原数组
static int[] a = new int[N];
// 队列
static int[] q = new int[N]; static int hh=0,tt=-1;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); String[] num = br.readLine().split(" "); // a数组中由n个元素
int n = Integer.parseInt(num[0]); // 滑动窗口的大小
int k = Integer.parseInt(num[1]);
String[] nums = br.readLine().split(" "); // 初始化a数组
for(int i = 0; i < n; i++) a[i] = Integer.parseInt(nums[i]); // 查找最小值
for(int i = 0; i < n; i++){ // 判断当前窗口是否大于滑动窗口的大小
if(hh <= tt && i-q[hh]+1 > k) hh++; // 判断如果有元素并且队尾元素大于入队元素,则舍弃队尾元素来保证单调上升的队列;
while(hh <= tt && a[q[tt]] >= a[i]) tt--; //向队列加入元素下标
q[++tt] = i; // 如果下标大于等于窗口大小,那么输出队头元素
if(i+1 >=k) bw.write(a[q[hh]]+" ");
}
bw.write("\n");
hh = 0;
tt = -1;
// 查找最大值
for(int i = 0; i < n; i++){ // 判断当前窗口是否大于滑动窗口的大小
if(hh <= tt && i-q[hh]+1 > k) hh++; // 判断如果有元素并且队尾元素小于入队元素,则舍弃队尾元素来保证单调上升的队列;
while(hh <= tt && a[q[tt]] <= a[i]) tt--; //向队列加入元素下标
q[++tt] = i; // 如果下标大于等于窗口大小,那么输出队头元素
if(i+1 >=k) bw.write(a[q[hh]]+" ");
}
bw.flush();
br.close();
bw.close();
} }

KMP算法:

KMP定义:是取自三个发明人的首字母组成的;

作用:是一个字符串匹配算法,对暴力的一种优化

kmp实现的方式:求next[]、以及匹配字符串

明确其中的概念:

对字符串的匹配,需要两个字符串:长字符串为模板字符串,短的字符串为匹配字符串;

前缀与后缀的概念:(很重要)

举个例子:枚举

ababa

其前缀为:

a,ab,aba,abab

其后缀:

baba,aba,ba,a

其中前缀与后缀的最长且相同的元素字符串是:aba ;长度length:3。

模板串的匹配:

匹配失败的话,假设p1串往后移动到p2串位置,表明1串=2.2串;

\[\because 匹配失败使得p1移动到p2位置\\
\therefore s-1 = p2-2.2\\
\because p2是p1的平移\\
\therefore p1 = p2\\
\therefore p1-2.1 = p2-2.2\\
又\because s = p1\\
\therefore s-1 = p1-3\\
由上可知:\\
p2-2.2 = p1-3;\\
p1-2.1 = p1-3;
\]

如果匹配失败,p串最少往右移动多少(看next[]数组),可以使得p串与s串相等,由图可知:往后移动多少是看p串,如果我们能预处理来这个东西:使得p1-2.1串能与p1.3串相等;这个相等的最大值是多少,值越大,则表示往后移动p串的距离越少;

// kmp匹配过程,遍历s模板每个元素
for(int i =1, j =0; i<=m; i++ ){
// 如果j回退到0或者i位置元素与j+1位置的元素不相同,那么执行回退操作,
//j退回next[i]处,即前缀与后缀相同的区间最后元素位置
while(j != 0 && s[i] != p[j+1]) j = next[j];
if(s[i] == p[j+1]) j++;
// 如果匹配成功
if(j ==n){
// 输出匹配元素在s模板中的起始位置
bw.write(i-n+" ");
// 继续匹配;
j = next[j];
}

也就是p串的前缀与后缀相同的最大字串是多少;----也就是next[]数组;

next[]数组(难点)

其中next[j]数组表示的是:子串p[1~j]的最长相等前后缀的前缀最后一位的下标。

对 p = “abcab”

p a b c a b

下标 1 2 3 4 5

next[ ] 0 0 0 1 2

对next[ 1 ] :前缀 = 空集—————后缀 = 空集—————next[ 1 ] = 0; (特殊点)

对next[ 2 ] :前缀 = { a }—————后缀 = { b }—————next[ 2 ] = 0;

对next[ 3 ] :前缀 = { a , ab }—————后缀 = { c , bc}—————next[ 3 ] = 0;

对next[ 4 ] :前缀 = { a , ab , abc }—————后缀 = { a . ca , bca }—————next[ 4 ] = 1;

对next[ 5 ] :前缀 = { a , ab , abc , abca }————后缀 = { a , ab , cab , bcab}————next[ 5 ] = 2;

// 实现next数组(找到前缀与后缀相同的最大元素长度)
int[] next = new int[n+1];
//next[1] = 0;
for(int i = 2,j =0; i<= n;i++){
// 如果j没有回退到0并且i位置元素与j+1位置的元素不相同,那么执行回退操作,
//j退回next[i]处,即前缀与后缀相同的区间最后元素位置
while(j !=0 && p[i] != p[j+1]) j = next[j];
// 如果i与j+1相同,那么移动j向后匹配
if(p[i] == p[j+1]) j++;
// p[1,j] = p[i-j+1,i];前缀与后缀相同;i表示终点
next[i] = j;
}

完整代码:

import java.util.*;
import java.io.*; //下标为什么从1开始,简化代码的复杂度;
public class Main{ public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // 对模式串p进行操作
int n = Integer.parseInt(br.readLine());
char[] p = new char[n+1];
// 将输入的字符保存到缓冲区
String pstr = br.readLine();
for(int i = 1; i <=n;i++){
// 取出的字符串下标-1;
p[i] = pstr.charAt(i-1);
} // 对模板串进行操作
int m = Integer.parseInt(br.readLine());
char[] s = new char[m+1];
String sstr = br.readLine();
for(int i = 1;i <= m;i++){
s[i] = sstr.charAt(i-1);
} // 实现next数组
int[] next = new int[n+1];
//next[1] = 0;
for(int i = 2,j =0; i<= n;i++){
// 如果j回退到0或者i位置元素与j+1位置的元素不相同,那么执行回退操作,
//j退回next[i]处,即前缀与后缀相同的区间最后元素位置
while(j !=0 && p[i] != p[j+1]) j = next[j];
// 如果i与j+1相同,那么移动j向后匹配
if(p[i] == p[j+1]) j++;
// p[1,j] = p[i-j+1,i];前缀与后缀相同;i表示终点
next[i] = j;
} // kmp匹配过程,遍历s模板每个元素
for(int i =1, j =0; i<=m; i++ ){
// 如果j回退到0或者i位置元素与j+1位置的元素不相同,那么执行回退操作,
//j退回next[i]处,即前缀与后缀相同的区间最后元素位置
while(j != 0 && s[i] != p[j+1]) j = next[j];
if(s[i] == p[j+1]) j++;
// 如果匹配成功
if(j ==n){
// 输出匹配元素在s模板中的起始位置
bw.write(i-n+" ");
// 继续匹配;
j = next[j];
} }
bw.flush();
br.close();
bw.close();
} }

结束:

感谢大家看到最后,如果有错误,欢迎指正!

参考文献:https://www.acwing.com/solution/content/14666/

链表、栈、队列、KMP相关知识点的更多相关文章

  1. 栈 队列 hash表 堆 算法模板和相关题目

    什么是栈(Stack)? 栈(stack)是一种采用后进先出(LIFO,last in first out)策略的抽象数据结构.比如物流装车,后装的货物先卸,先转的货物后卸.栈在数据结构中的地位很重要 ...

  2. java——链表、链表栈 LinkedListStack、链表队列 LinkedListQueue

    LikedList: package Date_pacage; public class LinkedList<E> { public static void main(String[] ...

  3. 【PHP数据结构】队列的相关逻辑操作

    在逻辑结构中,我们已经学习了一个非常经典的结构类型:栈.今天,我们就来学习另外一个也是非常经典的逻辑结构类型:队列.相信不少同学已经使用过 redis . rabbitmq 之类的缓存队列工具.其实, ...

  4. redis相关知识点

    redis 的相关知识点 启动 启动代码 redis-cli -a 密码 通用命令 expire: 设置有效期 expire name 10 key key * 相关数据类型 String set:添 ...

  5. java 集合 Connection 栈 队列 及一些常用

    集合家族图 ---|Collection: 单列集合 ---|List: 有存储顺序 , 可重复 ---|ArrayList: 数组实现 , 查找快 , 增删慢 ---|LinkedList: 链表实 ...

  6. Android开发涉及有点概念&相关知识点(待写)

    前言,承接之前的 IOS开发涉及有点概念&相关知识点,这次归纳的是Android开发相关,好废话不说了.. 先声明下,Android开发涉及概念比IOS杂很多,可能有很多都题不到的.. 首先由 ...

  7. IOS开发涉及有点概念&相关知识点

    前言,IOS是基于UNIX的,用C/C+/OC直通系统底层,不想android有个jvm. 首先还是系统架构的分层架构 1.核心操作系统层 Core OS,就是内存管理.文件系统.电源管理等 2.核心 ...

  8. Java 容器之 Connection栈队列及一些常用

    集合家族图 ---|Collection: 单列集合 ---|List: 有存储顺序 , 可重复 ---|ArrayList: 数组实现 , 查找快 , 增删慢 ---|LinkedList: 链表实 ...

  9. java面向对象的栈 队列 优先级队列的比较

    栈 队列 有序队列数据结构的生命周期比那些数据库类型的结构(比如链表,树)要短得多.在程序操作执行期间他们才被创建,通常用他们去执行某项特殊的任务:当完成任务之后,他们就会被销毁.这三个数据结构还有一 ...

随机推荐

  1. ModelArts 与HiLens Kit联合开发丨行人社交距离风险提示Demo

    摘要:本Demo使用YOLOv3_Resnet18模型来检测的视频流中的行人,获取行人坐标(即图中蓝色方框),然后计算所有检测到的人之间的相互"距离". 前情提要 听到行人社交距离 ...

  2. 利用Javascript制作网页特效(图像特效)

    图像是文本的解释和说明,在网页中的适当位置放置一些图像,不仅可以使文本更加容易阅读,而且可以使网页更加具有吸引力. 当鼠标指针经过图像时图像振动效果 ①:在head标签内输入以下代码: <sty ...

  3. kubenetes 相关命令(转载)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/xingwangc2014/article/details/51204224好久没写博客了,前段时间公 ...

  4. c++复习笔记(4)

    这一篇是另一篇各种琐碎东西的笔记. 类型转换可以通过类型转换函数,或者构造函数来实现.但是一般来说类型转换指的是类型转换函数. 类型转换函数不需要声明输出类型(因为输出类型是固定的),也没有参数,同时 ...

  5. 5.DHCP新建作用域及添加地址保留(Windows2012)

    1.新建作用域 右键IPv4 点击新建作用域 点击下一步 命名,下一步 填写子网,下一步 在上一步已经预留了,直接点下一步即可. 设置租用期限,建议为3天,下一步. 下一步,配置DHCP选项 配置默认 ...

  6. Inceptor [Code: 40000, SQL State: 42000] COMPILE FAILED: Internal error NullPointerException: [Error 40000] java.lang.NullPointerException

    下面代码报空指针 with `__all_dim__` as ( select * from ( select from_unixtime(unix_timestamp(`__bts__`) -1,' ...

  7. Codeforces Round #672 (Div. 2)

    比赛链接:https://codeforces.com/contest/1420 A. Cubes Sorting 题意 给出一个大小为 $n$ 的数组 $a$,每次只可以交换相邻的两个元素,最多交换 ...

  8. 第 45 届国际大学生程序设计竞赛(ICPC)亚洲网上区域赛模拟赛. A.Easy Equation (前缀和/差分)

    题意:RT,给你四个数\(a,b,c,d\),求\(x+y+z=k\)的方案数. 题解:我们可以先枚举\(x\)的值,然后\(x+y\)能取到的范围一定是\([x,x+b]\),也就是说这个区间内每个 ...

  9. C# 之 dynamic

    C#中的dynamic用于避免编译时类型检查,编译器在运行时获取类型. dynamic无法使用VisualStudio的intelliSense(智能感知),即调用dynamic修饰的对象的方法或字段 ...

  10. redis如何实现高可用【主从复制、哨兵机制】

    实现redis高可用机制的一些方法: 保证redis高可用机制需要redis主从复制.redis持久化机制.哨兵机制.keepalived等的支持. 主从复制的作用:数据备份.读写分离.分布式集群.实 ...