栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数组-链表(单链表,双链表)-栈-队列-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++;

链表:

    头插法:
    head开始=-1;index初始化
    插值法:看清出插入点
    ps:
    插入操作时,一定要先连接后面的数值,否则,记不住前一个节点指向的哪一个点。删除第一个节点时需要特殊判断:
    删除的是,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 
双链表 
双链表:
三个数组
没有头指针,有最左和最右节点。
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 
注意:
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 
队列:先进先出。 
两头操作,
插入:
队尾插入元素。
指向的是队尾的元素,
先++,再指向元素
弹出:
++
队头弹出元素。

模板:

// 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 

循环队列

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 
单调栈 

模板

//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 
单调队列 

注意:

//第一种会造成死循环
            while (left<=right){
                if(arr[i] 

模板:

常见模型:找出滑动窗口中的最大值/最小值
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;iqueue[left]){
                left++;
            }
            //while (left<=right && arr[i]
            while (left<=right){
                if(arr[i]=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 

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

求解最小值时:
存在逆序对时,直接删除前面的大值。
找最小值,直接找对头,与单调队列类似。维护
出队列:
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();
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/770170.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号