数组-链表(单链表,双链表)-栈-队列-kmp

数组模拟链表:

更加方便,更加快捷,不需要新建数据结构。大部分都是这样写。

单链表

链表:

两个数组,

有头指针head
e【】//存储当前节点的值
next【】//存储当前节点指向的下一个位置,也是指针
idx //表示当前节点的存储空间
//一个节点存储两个东西:
//head头指针存储下一个位置
ps:可以用O(1)的时间查到下一个点的位置,找不到前一个的位置。
  输出时注意,不能按照顺序输出,要按照定义进行输出,未节点最后指向的是空集,也就是-1;
    for(int i = head; i != -1; i = next[i]){
        System.out.print(e[i] + " ");
    }
}

【】一个值,【】一个next指针
插入:任意指针可以插入。

在这里插入图片描述
在这里插入图片描述

1.head 存储的就是下一个指针
2.每次新开辟一个位置idx是为了给新节点使用。所以每次结尾都需要idx++;

链表:

  1. 头插法:
    head开始=-1;index初始化
    插值法:看清出插入点
    ps:
    插入操作时,一定要先连接后面的数值,否则,记不住前一个节点指向的哪一个点。
  2. 删除第一个节点时需要特殊判断:
    删除的是,head指向的下一个点,head指针。
    题目:
    在这里插入图片描述
    模板
// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点
int head, e[N], ne[N], idx;

// 初始化
void init()
{
    head = -1;
    idx = 0;
}

// 在链表头插入一个数a
void insert(int a)
{
    e[idx] = a, ne[idx] = head, head = idx ++ ;
}

// 将头结点删除,需要保证头结点存在
void remove()
{
    head = ne[head];
}

java:

package 大学菜;
//1。等于时,使用最好的判断方法是equal
//2。节点位置的判断,插入第k个
//3。输出时,注意按照定义进行输出
//4。初始化是,头指针指向空。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 数组模拟链表4 {
    static int N=100010;
    static int[] e = new int[N];
    static  int[] next = new int[N];
    static int head;
    static int index;
    public static void init(){
        head=-1;//最后这个-1会跑到最后
        index=0;//index没有必要在开始赋值,初始哈赋值即可
    }
    public static void Insert(int k,int x){
        //插入第k个点,传进来的时候是k-1,下面才是第k个
        e[index] = x;
        next[index] =next[k];
        next[k] =index;
        index++;
    }
    public static void Delete(int k){
        //考虑头节点的特殊情况
        next[k] = next[next[k]];
    }
    public static void HeadInsert(int x){
        e[index] = x;
        next[index] = head;
        head = index;
        index++;
    }

    public static void main(String[] args) throws IOException {
        init();
        //输入:
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(reader.readLine());
        for(int i=0;i<n;i++){
            String[] str = reader.readLine().split(" ");
            String fir = str[0];
            if(fir.equals("H")){//equals判断方法更好
                int x = Integer.valueOf(str[1]);
                HeadInsert(x);
            }else if(fir.equals("I")){
                int k = Integer.valueOf(str[1]);
                int x = Integer.valueOf(str[2]);
                Insert(k-1,x);
            }else if(fir.equals("D")){
                int k = Integer.valueOf(str[1]);
                if(k==0){
                    head=next[k];
                }else {Delete(k-1);}
            }
        }
        //
        for(int i=head;i != -1;i=next[i]){//输出遵守定义
            System.out.print(e[i]+" ");
        }
    }
}

双链表

双链表:
三个数组
没有头指针,有最左和最右节点。
idx 当前的位置,也是当前节点的位置。
e【】//存储当前节点的值
l【】//存储当前节点左节点的指针
r【】//存储当前右节点的指针
1.插入节点:
插入右节点,插入左节点,都可以用插入右节点使用。
2.删除节点
第k个实际上是k-1
3.最左,最右插入

模板:

/ e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;

// 初始化
void init()
{
    //0是左端点,1是右端点
    r[0] = 1, l[1] = 0;
    idx = 2;
}

// 在节点a的右边插入一个数x
void insert(int a, int x)
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

// 删除节点a
void remove(int a)
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}

借用别人的图进行介绍
:初始化有两个节点
所以第三个用,也就是存储数据的第一个用下标2表示。
在这里插入图片描述

delete()add_kl()add_kr()
函数在内部实现时,是同过下标index来操作的,
所以传参时,要通过关系index=k+1 把正确的index值传进去

题目:

实现一个双链表,双链表初始为空,支持 5 种操作:

在最左侧插入一个数;
在最右侧插入一个数;
将第 k 个插入的数删除;
在第 k 个插入的数左侧插入一个数;
在第 k 个插入的数右侧插入一个数
现在要对该链表进行 M 次操作,进行完所有操作后,从左到右输出整个链表。

注意:题目中第 k 个插入的数并不是指当前链表的第 k 个数。例如操作过程中一共插入了 n 个数,则按照插入的时间顺序,这 n 个数依次为:第 1 个插入的数,第 2 个插入的数,…第 n 个插入的数。

输入格式
第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:

L x,表示在链表的最左端插入数 x。
R x,表示在链表的最右端插入数 x。
D k,表示将第 k 个插入的数删除。
IL k x,表示在第 k 个插入的数左侧插入一个数。

代码

package 大学菜;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 数组模拟双链表 {
    //e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
    static int N = 100010;
    static int[] e = new int[N];
    static int[] l_next = new int[N];
    static int[] r_next = new int[N];
    static int index ;
    public static void init(){
        //直接用数组进行模拟左右端点
        r_next[0] = 1;//
        l_next[1] = 0;//
        index =2;///初始化第1个插入节点下标为2,所以操作第k个数时都要+1 !!
    }
    public static void Linsert(int x){
        //在最左边插入
        e[index] = x;
        //当前节点的右指针
        l_next[index] = 0;
        r_next[index] = r_next[0];
        l_next[r_next[0]] = index;
        r_next[0] = index;
        index++;
    }
    public static void Rinsert(int x){//最右侧插入
        e[index] =x;
        //新建节点右连接1点【=============葛·8
        r_next[index] = 1;
        l_next[index] = l_next[1];
        r_next[l_next[1]] = index;
        l_next[1] = index;
        index++;
    }
    public static void ILinsert(int k,int x){//传入的就是k-1
        //
        e[index] = x;
        l_next[index] = l_next[k];
        r_next[index] = k;
        r_next[l_next[k]] = index;
        l_next[k] = index;
        index++;
    }
    public static void Rlinsert(int k,int x){
        e[index] = x;
        r_next[index] = r_next[k];
        l_next[index] = k;
        l_next[r_next[k]] = index;
        r_next[k] = index;
        index++;
    }
    public static void Delete(int k ){
        r_next[l_next[k]] = r_next[k];
        l_next[r_next[k]] = l_next[k];
    }

    public static void main(String[] args) throws IOException {
        //buff
        init();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(reader.readLine());
        for(int i=0;i<n;i++){
            String[] str = reader.readLine().split(" ");
            String s = str[0];
            if(s.equals("L")){
                int x = Integer.valueOf(str[1]);
                Linsert(x);
            }else if(s.equals("R")){
                int x= Integer.valueOf(str[1]);
                Rinsert(x);
            }else if(s.equals("D")){
                int k = Integer.valueOf(str[1]);
                Delete(k+1);
            }else if(s.equals("IL")){
                int k = Integer.valueOf(str[1]);
                int x = Integer.valueOf(str[2]);
                ILinsert(k+1,x);//k=2
            }else{//那么最后还是使用else吧
                int k = Integer.valueOf(str[1]);
                int x = Integer.valueOf(str[2]);
                Rlinsert(k+1,x);
            }
        }
        for(int i=r_next[0];i!=1;i=r_next[i]){//第一个数等于的是0de右边第一个
            System.out.print(e[i]+" ");
        }
    }
}

注意:
1.1作为的是最右节点
2.0作为的是最左节点
3.初始化时l[0] =1;r[1] =0;
4.输出时,从0的右边第一个进行输出。结尾从1的左边第一个结尾。

邻接表

存储树和图,使用数组进行存储。

数组模拟栈

栈:先进后出

1.插入:
每次指针都指向最后一个有数据的元素,所以先++,再指向新元素。
2.弹出:
栈顶元素先弹出,再--。
3.判断栈是否为空:tt指针是否为0;相当于下标。
4.查询数组元素:

模板:

// tt表示栈顶
int stk[N], tt = 0;

// 向栈顶插入一个数
stk[ ++ tt] = x;

// 从栈顶弹出一个数
tt -- ;

// 栈顶的值
stk[tt];

// 判断栈是否为空
if (tt > 0)
{

}

题目:

实现一个栈,栈初始为空,支持四种操作:

push x – 向栈顶插入一个数 x;
pop – 从栈顶弹出一个数;
empty – 判断栈是否为空;
query – 查询栈顶元素。
现在要对栈进行 M 个操作,其中的每个操作 3 和操作 4 都要输出相应的结果。
输入格式
第一行包含整数 M,表示操作次数。
接下来 M 行,每行包含一个操作命令,操作命令为 push x,pop,empty,query 中的一种。
输出格式
对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。
其中,empty 操作的查询结果为 YES 或 NO,query 操作的查询结果为一个整数,表示栈顶元素的值。

数据范围
1≤M≤100000,
1≤x≤109
所有操作保证合法。
package 大学菜;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 模拟栈 {
    //数组模拟栈
    static int N =100010;
    static int[] stack  = new int[N];
    static int top;//栈顶指针
    public static void init(){
        top =-1;
    }
    public static void push(int x){
        //top++;
        top++;
        stack[top] = x;
    }
    public static void query(){
        //查看栈顶元素
        System.out.println(stack[top]);
    }
    public static void pop(){
        //删除栈顶
        top--;
    }
    public static boolean empyty(){
        if(top==-1){
            return true;
        }else return false;
    }
    public static void main(String[] args) throws IOException {
        init();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(reader.readLine());
        for(int i=0;i<n;i++){
            String[] str = reader.readLine().split(" ");
            //判断
            String s0 = str[0];
            if(s0.equals("push")){
                int x = Integer.valueOf(str[1]);
                push(x);
            }else if(s0.equals("query")){
                query();//询问栈顶元素
            }else if(s0.equals("pop")){
                pop();//弹出栈顶元素
            }else{
                if(empyty()){
                    System.out.println("YES");
                }else {
                    System.out.println("NO");
                };//判空
            }
        }
    }
}

队列:先进先出。

两头操作,
插入:
队尾插入元素。
指向的是队尾的元素,
先++,再指向元素
弹出:
++
队头弹出元素。

模板:

// hh 表示队头,tt表示队尾
int q[N], hh = 0, tt = -1;

// 向队尾插入一个数
q[ ++ tt] = x;

// 从队头弹出一个数
hh ++ ;

// 队头的值
q[hh];

// 判断队列是否为空
if (hh <= tt)
{

}

题目:

实现一个队列,队列初始为空,支持四种操作:
push x – 向队尾插入一个数 x;
pop – 从队头弹出一个数;
empty – 判断队列是否为空;
query – 查询队头元素。
现在要对队列进行 M 个操作,其中的每个操作 3 和操作 4 都要输出相应的结果。
输入格式
第一行包含整数 M,表示操作次数。
接下来 M 行,每行包含一个操作命令,操作命令为 push x,pop,empty,query 中的一种。
输出格式
对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。
其中,empty 操作的查询结果为 YES 或 NO,query 操作的查询结果为一个整数,表示队头元素的值。
数据范围
1≤M≤100000,
1≤x≤109,
所有操作保证合法。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    //两个指针
    static int N = 100010;
    static int Dl[] = new int[N];
    static int left;
    static int right;
    //初始化,指针都指向-1;
    public static void init(){
        left = -1;//队头
        right = -1;//队尾
    }
    public static void push(int x){
        right++;
        Dl[right] = x;
        //push x – 向队尾插入一个数 x;
    }
    public static void pop(){
        left++;
        //pop – 从队头弹出一个数;
    }
    public static boolean empty(){
        //empty – 判断队列是否为空;
        if(left==right){return true;
        }else return false;
    }
    public static void query(){
        //query – 查询队头元素。
        System.out.println(Dl[left+1]);

    }

    public static void main(String[] args) throws IOException {
        //n
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(reader.readLine());
        for(int i=0;i<n;i++){
            String[] str = reader.readLine().split(" ");
            String s0 = str[0];
            if(s0.equals("push")){
                int x = Integer.valueOf(str[1]);
                push(x);
            }else if(s0.equals("pop")){
                pop();
            }else if(s0.equals("empty")){
                if(empty()){
                    System.out.println("YES");
                }else {System.out.println("NO");};//输出
            }
            else {
                query();//输出
            }
        }

    }
}

循环队列

package 大学菜;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 数组模拟循环队列 {
    //两个指针
    static int N = 100010;
    static int Dl[] = new int[N];
    static int left;
    static int right;
    //初始化,指针都指向-1;
    public static void init(){
        left = 0;//队头
        right = 0;//队尾
    }
    public static void push(int x,int n){
        right++;//9
        Dl[right] = x;
        if(right==n){
            right=0;
        }

        //push x – 向队尾插入一个数 x;
    }
    public static void pop(int n){
        left++;
        if(left==n){left=0;}
        //pop – 从队头弹出一个数;
    }
    public static boolean empty(){
        //empty – 判断队列是否为空;
        if(left==right){return true;
        }else return false;
    }
    public static void query(){
        //query – 查询队头元素。
        System.out.println(Dl[left+1]);

    }

    public static void main(String[] args) throws IOException {
        //n
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(reader.readLine());
        for(int i=0;i<n;i++){
            String[] str = reader.readLine().split(" ");
            String s0 = str[0];
            if(s0.equals("push")){
                int x = Integer.valueOf(str[1]);
                push(x,n);
            }else if(s0.equals("pop")){
                pop(n);
            }else if(s0.equals("empty")){
                if(empty()){
                    System.out.println("YES");
                }else {System.out.println("NO");};//输出
            }
            else {
                query();//输出
            }
        }

    }
}

单调栈

模板

//int tt = 0;
for (int i = 1; i <= n; i ++ )
{
    while (tt && check(stk[tt], i)) tt -- ;
    stk[ ++ tt] = i;
}

题目:

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。
输入格式
第一行包含整数 N,表示数列长度。
第二行包含 N 个整数,表示整数数列。
输出格式
共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1

q求找到,每一个数,左边离他最近的数。直接将大的数进行删除,栈中存储的是单调的序列,每次更新栈顶


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    static int N = 100010;
    static int[] A = new int[N];
    static int[] stack = new int[N];
    static int top;
    public static void init() {
        top =0;
        stack[0] = -1;
    }
    public static int Min(int x){
        //判断x的大小
        while (x<=stack[top]){//当x比当前的栈顶元素小时,则需要出栈
            top--;
        }
        int c = stack[top];
        top++;
        stack[top] = x;
        return c;
    }

    public static void main(String[] args) throws IOException {
        init();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(reader.readLine());
        String[] str = reader.readLine().split(" ");
        for(int i=0;i<n;i++){
            A[i] = Integer.valueOf(str[i]);
            int c = Min(A[i]);
            System.out.print(c+" ");
        }
    }
}

单调队列

注意:

//第一种会造成死循环
            while (left<=right){
                if(arr[i]<arr[queue[right]]){
                    right--;
                }
            }
//下面这个可以进行跳出循环
            while (left<=right){
                if(arr[i]<arr[queue[right]]){
                    right--;
                }else {
                    break;
                }
            }

模板:

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  
    // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}

1.求滑动窗口里面的最大值和 最小值
题目:


给定一个大小为 n≤106 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7],k 为 3

java

package 大学菜;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 单调队列2 {
    static int N=100010;
    static int[] A = new int[N];
    static int[] queue = new int[N];
    static int left=0;//队头指针
    static int right=-1;//队尾指针
    public static void function(int[] arr,int n,int k){
        //开始的时候,队列中没有元素
        for(int i=0;i<n;i++){
            //每次看看是否应该保留当前的queue中的位置,如果当前窗口的首元素位置已经不包含之前的最小元素,就让队头元素出战。
            if (left<=right && i-k+1 >queue[left]){
                left++;
            }
            //while (left<=right && arr[i]<arr[queue[right]]) right--;//刚才
            while (left<=right){
                if(arr[i]<arr[queue[right]]){
                    right--;
                }else {
                    break;
                }
            }
            //找到合适的地方后
            right++;
            queue[right] = i;//存储的是下标的位置。
            //判断是否已经数字到能k;
            if(i+1>=k){
                //输出元素
                System.out.print(arr[queue[left]]+" ");
            }
        }
    }
    //开始的时候,队尾应在-1的位置。下一次加加才可以有元素。
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] str = reader.readLine().split(" ");
        int n =Integer.valueOf(str[0]);
        int k= Integer.valueOf(str[1]);
        //System.out.println(n+" " +k);
        String[] str1 = reader.readLine().split(" ");
        for(int i=0;i<n;i++){
            A[i] = Integer.valueOf(str1[i]);//System.out.print(A[i]);
        }
        //维护最小数值
        //queue 存储的是维护的最小值的下标。
        //i-k+1 是当前窗口的首元素位置。
        //left<right 说明队列里面没有元素。
        function(A,n,k);
    }
}

注意:
维护一个窗口的队列,然后可以使用弹出弹入 再进行求解大值,和小值

求解最小值时:
存在逆序对时,直接删除前面的大值。
找最小值,直接找对头,与单调队列类似。维护
出队列:
i-k+1;
最大值:维护一个最大的值。
队头维护一个最大的值。
顺序不能改变。

KMP算法

字符串匹配。
分别有一个长串和一个短串,
短串去匹配长串。
1.先求出nextj
2.在进行匹配

先思考朴素做法,在进行思考,更快捷的做法。
在这里插入图片描述
在这里插入图片描述
需要进行三段进行比较,三段是否相等的地方可以省去一步一步的进行移动,时间可以大量节省。

最长后缀

在这里插入图片描述
java

package 大学菜;

import java.io.*;
public class kmp {
    static int N = 1000100;
    static char[] p = new char[N];
    static char[] pp = new char[N];
    static char[] s = new char[N];
    static char[] ss = new char[N];
    static int[] next = new int[N];//
    static int[] prefix = new int[N];//nextj数组
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
        int np =Integer.valueOf(reader.readLine());
        String str = reader.readLine();
        pp= str.toCharArray();//利用toCharArray方法转换
        for( int i=1;i<=np;i++){
            p[i] = pp[i-1];
        }
        int ns = Integer.valueOf(reader.readLine());
        String str2 = reader.readLine();
        ss = str2.toCharArray();//从0开始
        for(int i=1;i<=ns;i++){
           s[i] = ss[i-1];
        }

        // 构造前缀数组
        int prefix[] = new int[np + 1];
        for (int i = 2, j = 0; i <= np; i++) {
            //i从2开始,因为prefix[1]肯定为0
            while (j != 0 && p[i] != p[j + 1])
                j = prefix[j];
            if (p[i] == p[j + 1])
                j++;
            prefix[i] = j;
        }

        // kmp匹配
        for (int i = 1, j = 0; i <= ns; i++) {
            while (j != 0 && s[i] != p[j + 1]) {
                j = prefix[j]; // s[i] != p[j + 1]即不匹配,则往后移动
            }
            if (s[i] == p[j + 1])
                j++; // 匹配时将j++进行下一个字符得匹配
            if (j == np) { // 匹配了n字符了即代表完全匹配了
                writer.write(i - np +" ");
                j = prefix[j]; // 完全匹配后继续搜索
            }
        }
        writer.flush();
        writer.close();
        reader.close();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值