算法--数据结构基础

文章目录

  • 数据结构
    • 单链表
      • 表达式求值
        • 前缀表达式
        • 中缀表达式
        • 后缀表达式
    • 队列
    • 单调栈
    • 单调队列
    • KMP
    • Trie
    • 并查集
    • 哈希表
      • 字符串哈希

数据结构

单链表

用数组模拟(静态链表)效率比定义Node类(动态链表)效率高些
在这里插入图片描述

使用数组模拟单链表,e [ ] 数组中存值,ne [ ] 数组中存下个元素位置下标,定义头指针head,初始时指向-1,定义idx表示用到了哪个下标

定义数组 stk[ ] tt指向栈顶初始为-1,插入时tt++,弹出时tt- - ,查看栈是否为空,只用看tt是否大于0即可,栈顶元素即stk[tt]

表达式求值

前缀表达式

运算符位于操作数之前,求值过程:

  1. 从右向左读取表达式
  2. 将遇到的数字压入栈中,读到运算符是弹出栈顶操作数并进行计算
中缀表达式

常见的数学表达式,运算符位于操作数之间

后缀表达式

也被称为逆波兰表达式,运算符位于操作数之后,后缀表达式不需要括号,不存在优先级问题

求值过程与前缀表达式相同,不过是从左向右读取

后缀表达式在计算机科学中有广泛的应用,特别是在编译器设计、计算器实现和栈的应用中。它可以方便地用于计算复杂的算术表达式,并且可以通过简单的迭代和栈操作来实现。可以将中缀表达式转换为后缀表达式,使其更适合计算机程序中的求值过程。

队列

定义数组q[ ] ,hh为头下标初始为0,tt为尾,初始为-1,尾部添加元素,添加时,q[++tt] = x,头部删除,删除hh++即可

查看队列是否为空,只用看hh<=tt,如果是,不为空,不是就为空,查看队头元素只用看q[hh]

单调栈

情景:给一个序列,找到一个数左边(右边)满足xx条件,且离他最近的一个数

例如:给一个序列,找到每个数左边离他最近的且比他小的数,不存在的话返回-1

  • 暴力:双层for循环,一层逐个遍历,一层从遍历的位置的前一个开始倒着遍历,直到找到比他小的

  • 单调栈思路:
    在读数据的同时维护一个栈,如果栈不为空,就比较栈顶元素和当前要加入的元素的大小,如果大于或等于当前元素,就将栈顶元素弹出,直到新的栈顶元素比当前元素小,就停止循环弹出栈顶元素,如果此时栈不为空,那么栈顶元素即答案,栈为空答案为-1,最后将当前元素入栈

    这样维护的栈一定是单调的

单调队列

情景:求滑动窗口中的最大值和最小值

例如:给定一串数字,有个大小为k的滑动窗口,从左边移到右边,求出每个位置的滑动窗口的最大值和最小值

  • 普通队列:维护一个队列,当窗口向右走一步,就将新的元素添加进队尾并删掉队头,暴力求窗口中的最大和最小值即可
  • 单调队列:
    队列中存元素索引,遍历整个数组,先判断队头元素是否已经被移除窗口,如果是,将队头元素从队列中移除,
    获取窗口最小值:判断队尾元素与当前元素大小,若队尾元素大于当前元素,删除队尾元素,直到队尾元素小于当前元素,再将当前元素添加到队尾,然后判断当前遍历的元素是否达到窗口大小,达到就输出队头(窗口最小值)即可
    获取窗口最大值:和上面一样,只是判断队尾元素与当前元素大小相反

队尾添加元素,添加的过程中保证目前的结果一定在队头,队头取结果即可

KMP

习惯下标从1开始

对模板串处理:对每个点预处理以某点为终点的后缀和前缀相等,相等的长度最大为多少

next[ i ] = j 以 i 为终点的后缀,和从一开始的前缀相等,而且后缀长度最长 ,记录的是最长公共前后缀长度

next[i] = j;
p[1,j] = p[i - j + 1];

Trie

用于高效存储查找字符串

模版:

解释:[0] [1] = 3 表示根节点有个儿子 b ,这个儿子在数组中的下标是3

​ [3] [4] = 7 [3] 表示当前字符的下标,[4] 表示当前字符有个儿子e,下标为7

public class Main{
    final static int N = 100010;
    static int[][]son = new int[N][26]; //这里总共有26个字符
    static int[]cnt = new int[N]; //以某个下标的字符为结尾的字符串个数
    static int idx = 0; //表示下标,自增来生成下标
    
    public static void insert(char[]str) {
        int p = 0;
        for(int i = 0;i < str.length;i++) {
            int u = str[i] - 'a';
            if(son[p][u] == 0) son[p][u] = ++idx;
            p = son[p][u];
        }
        cnt[p]++;
    }
    
    public static int query(char[]str) {
        int p = 0;
        for(int i = 0;i < str.length;i++) {
            int u = str[i] - 'a';
            if(son[p][u] == 0) return 0;
            p = son[p][u];
        }
        return cnt[p];
    }
}

并查集

用来快速处理 近乎O1

  • 将两个集合合并
  • 询问两元素是否在一个集合当中

实现方式:每个集合用一棵树来表示,树根的编号就是整个集合的编号,每个节点存储他的父节点,p[x] 表示x的父节点

a1:如何判断树根 :p[x] == x

a2:如何求x的集合编号:while(p[x] != x) x = p[x] (一直向上找他的父节点,直到找到了根)

a3:如何合并两集合:把其中一个集合的集合编号等于另一棵树的集合编号

求集合编号时时间复杂度和树高是成正比的,可能会出现树高过高问题,需要优化
**优化:**路径压缩

在从一个节点不断向上找到根节点时,将走过的所有节点直接指向根节点

模版:

public class Main{
    final static int N = 100010;
    static int[]p = new int[N];
    //找x的根节点 + 路径压缩
    static int find(int x) {
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }
    
    public static void main(String[]args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for(int i = 0;i < n;i++) p[i] = i;//初始化
        int m = sc.nextInt();
        for(int i = 0;i < m;i++) {
            String l = sc.next();
            int a = sc.nextInt();
            int b = sc.nextInt();
            if("M".equals(l)) p[find(a)] = find(b);//合并两集合
            else {
                if(find(a) == find(b)) System.out.println("Yes");
                else System.out.println("No");
            }
        }
    }
}

有些题往往还需要维护别的变量

完全二叉树,最后一层节点从左到右依次排列

用数组存储堆

这里索引从1开始,左儿子为 2x 右儿子为2x + 1 父节点为 x / 2

以最小堆为例,修改根节点元素,需要将新的值下沉,就让根元素和左右孩子比较,与最小的那个交换,一直到没法移了

上浮:和父节点比较,如果小于父节点,和父节点交换位置

插入:在heap[++size] = x,再将这个数不断上浮

删除根节点 :用堆的最后一个元素覆盖堆的根节点,在将其不断下沉

删除任意一个元素:heap[k] = heap[size] ; size - - ; up(k) || down(k);

修改任意一个元素:heap[k] = x; down(k) || up(k);

将数组转化成堆:

  • 可以一个一个往堆里add,复杂度为nlogn
  • 也可以从n/2 个元素开始倒着到1不断下沉操作

要修改第k个插入的元素,还需要存个映射关系:

  • 第k个插入的元素的索引 ph[ ]
  • 索引为x的元素是第几个插入的 hp[ ]

交换堆中元素时,也需要考虑到映射关系的改变:

public static void heapSwap(int x,int y) {
    swap(ph,hp[x],hp[y]);
    swap(hp,x,y);
    swap(a,x,y);
}

交换数组中的元素:

public static void swap(int[]q,int a,int b) {
    int t = q[a];
    q[a] = q[b];
    q[b] = t;
}

哈希表

离散化是一种保序的hash方式(只是其中一种)

情景:把0~10^9映射到 0 ~ 10^5 这些数

  • 存储结构
    • 开放寻址法
    • 拉链法
  • 字符串哈希方式

a1:hash函数一般怎么写

x mod 10^5(取模的这个数尽可能是质数,且离2整次幂尽可能远)

a2:处理冲突

  • 开放寻址法
  • 拉链法:将发生冲突的直接接在要插入的位置

在算法中,对哈希表一般只有添加和查找两个操作

算法中,对哈希表就算要删除,往往不会真的删,会再开一个数组,对每个位置打一个标记,标记一下被删除

拉链法:

import java.util.Scanner;
import java.util.Arrays;
public class Main{
    final static int N = 100003;
    static int[]h = new int[N];//哈希表的槽
    static int[]e = new int[N];//链表存值
    static int[]ne = new int[N];//链表存下一个元素位置
    static int idx;//链表当前用到的索引
    static int hash(int x) {
        return (x%N+N) % N;//计算hash值,即该存入位置索引,这样写目的是防止负数出现
    }
    //头插
    static void insert(int x) {
         int k = hash(x);
         e[idx] = x;
         ne[idx] = h[k]; //h[k]就是每个链表的头指针
         h[k] = idx++;
    }
    static boolean find(int x) {
        int k = hash(x);
        for(int i = h[k];i != -1;i = ne[i]) {
            if(e[i] == x) return true;
        }
        return false;
    }
    public static void main(String[]args) {
        Scanner sc = new Scanner(System.in);
        Arrays.fill(h,-1);//相当于初始化每条链表头结点为-1
        int n = sc.nextInt();
        for(int i = 0;i < n;i++) {
            String l = sc.next();
            int x = sc.nextInt();
            if(l.equals("I")) insert(x);
            else {
                if(find(x)) System.out.println("Yes");
                else System.out.println("No");
            }
        }
    }
}

开放寻址法:

只用一个一维数组,数组长度一般是题目要求的2~3倍(经验值)

添加:

先用hash得到该存入的索引,若该索引已有元素,依次找下一个位置,直到找到空的位置,将元素插入

查找:

用hash得到对应索引,若对应索引元素不是要查找的元素,依次往后找,直到找到空的位置,那么这个元素不存在

删除:

先查找x,然后对x打一个标记,表示他被删除了

0x3f3f3f3f的十进制为1061109567,和INT_MAX一个数量级,即10^9 数量级,而一般场合下的数据都是小于10^9的。
0x3f3f3f3f * 2 = 2122219134,无穷大相加依然不会溢出。

public class Main{
    static final int nem = 0x3f3f3f3f;//定义一个数据范围之外的数,表示当前位置为空
    static final int N = 200003;
    static int[]h = new int[N];
    static int hash(int x) {
        return (x%N+N) % N;
    }
    //核心
    //如果是添加,返回的就是该添加的位置,如果是查找,返回位置要么就是这个元素的位置,要么为空位置
    static int find(int x) {
        int k = hash(x);
        while(h[k] != nem && h[k] != x) {
            k++;
            if(k == N) k = 0;
        }
        return k;
    }
    public static void main(String[]args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        Arrays.fill(h,nem);
        while(n --> 0) {
            String[]rr = br.readLine().split(" ");
            int x = Integer.parseInt(rr[1]);
            int we = find(x);
            if(rr[0].equals("I")) {
                h[we] = x;
            }else {
                if(h[we] == x) System.out.println("Yes");
                else System.out.println("No");
            }
        }
    }
}

字符串哈希

当我们需要快速判断两个字符串是否相等时,可以使用

字符串前缀哈希法:先预处理出字符串每个前缀的hash值

在这里插入图片描述

如何求字符串的hash值:

  • p进制法:
    对于“ABCD”,使用p进制表示,可以表示成,(A * p^3 + B * p^2 + C * p^1 + D * p^0)mod Q,其中,将A映射成1,B - - > 2,C - - > 3,D - - > 4
    结果可能比较大,故给他模上一个Q,使结果在 0 ~ Q - 1的范围内
    • 不能映射成0,如果A映射成0,那么AA也是0,AAA也是0……,可以将他们映射成对应的ASII值
    • 当p取131或13331,Q取2^64(经验值),在这种情况下,我们可以不考虑冲突

我们可以利用预先求得的hash值,可以根据公式求得所有子串的hash值。

例如:

aabbaabb,要求3 ~ 7(L ~ R)位置的子串和hash值,即bbaab,需要知道hash[2] (aa) 和 hash[7] (aabbaab),转化为p进制就是 (11)p和(1122112)p,要求bbaab的hash值,就是求(22112)p

可以将(11)p左移成(1100000)p,即左移R - L + 1位(位运算理解),即乘以 p^R - L + 1

要求子串的hash值就可以表示为 hash[R] - hash[L - 1] * p^R - L + 1

示例:

public class Main{
    static final int N = 100010;
    static int[]h = new int[N];//预处理的前缀子串hash值
    static int[]p = new int[N];//p[i]表示p的i次方,将p的i次方预先算出来存到数组中
    static final int P = 131;
    public static void main(String[]args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[]ro1 = br.readLine().split(" ");
        int n = Integer.parseInt(ro1[0]);
        int m = Integer.parseInt(ro1[1]);
        String s = br.readLine();
        p[0] = 1;
        for(int i = 1;i <= n;i++) {
            h[i] = h[i - 1] * P + s.charAt(i - 1);//求前缀子串hash值
            p[i] = p[i - 1] * P;
        }
        while(m --> 0) {
            String[]ro2 = br.readLine().split(" ");
            int l1 = Integer.parseInt(ro2[0]);
            int r1 = Integer.parseInt(ro2[1]);
            int l2 = Integer.parseInt(ro2[2]);
            int r2 = Integer.parseInt(ro2[3]);
            if(h[r1] - h[l1 - 1] * p[r1 - l1 + 1] == h[r2] - h[l2 - 1] * p[r2 - l2 + 1]) System.out.println("Yes");
            else System.out.println("No");
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/250919.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年总结,讲讲我的故事吧,十年

文章目录 2023前十年后十年 周末&#xff0c;本该是提升自己的最好时机&#xff0c;也该是出去玩的大好时光&#xff0c;但是毫无意外的&#xff0c;在家躺了一天&#xff0c;单纯的有点累。 2023年&#xff0c;发生了好多事情&#xff0c;又好像没发生几件事&#xff0c;可能毕…

插入排序:直接插入排序 希尔排序

插入排序&#xff1a; 假设红竖线前的元素全部排好序&#xff0c;红线后面的数即为要插入的数据&#xff0c;红线依次往后移&#xff0c;假设end为排好序的最后一个数字&#xff0c;end1即为要插入的数字&#xff0c;一次插入时&#xff0c;end与要插入的数字依次比较&#xf…

springMVC-Restful风格

基本介绍 REST&#xff1a;即Representational State Transfer。&#xff08;资源&#xff09;表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便&#xff0c;所以正得到越来越多网站的采用. 1.HTTP协议里面&#xff0c;四个表示操…

Java技术栈 —— 微服务框架Spring Cloud —— Ruoyi-Cloud 学习(二)

RuoYi项目开发过程 一、登录功能(鉴权模块)1.1 后端部分1.1.1 什么是JWT?1.1.2 什么是Base64?为什么需要它&#xff1f;1.1.3 SpringBoot注解解析1.1.4 依赖注入和控制反转1.1.5 什么是Restful?1.1.6 Log4j 2、Logpack、SLF4j日志框架1.1.7 如何将项目打包成指定bytecode字节…

ValueError: setting an array element with a sequence...

报错&#xff1a;ValueError: setting an array element with a sequence… 案例1&#xff1a;numpy库使用numpy.array转换list a是一个list&#xff0c;其包含两个元组&#xff0c;使用np.array进行转换&#xff0c;会报错&#xff1a; import numpy as np a ([([1, 2, 3]…

nodejs微信小程序+python+PHP的微博网络舆情分析系统-计算机毕业设计推荐

&#xff08;4&#xff09;微博信息交流&#xff1a;在首页导航栏上我们会看到“微博信息交流”这一菜单&#xff0c;我们点击进入进去以后&#xff0c;会看到所有管理员在后台发布的交流信息&#xff1b; &#xff08;5&#xff09;新闻资讯&#xff1a;用户可以查看新闻资讯信…

关于“Python”的核心知识点整理大全24

10.1.6 包含一百万位的大型文件 前面我们分析的都是一个只有三行的文本文件&#xff0c;但这些代码示例也可处理大得多的文件。 如果我们有一个文本文件&#xff0c;其中包含精确到小数点后1 000 000位而不是30位的圆周率值&#xff0c;也可 创建一个包含所有这些数字的字符串。…

HiveSql语法优化三 :join优化

前面提到过&#xff1a;Hive拥有多种join算法&#xff0c;包括Common Join&#xff0c;Map Join&#xff0c;Bucket Map Join&#xff0c;Sort Merge Buckt Map Join等&#xff1b;每种join算法都有对应的优化方案。 Map Join 在优化阶段&#xff0c;如果能将Common Join优化为…

Java 基础学习(十二)文本I/O、日期与时间API

1 文本 I/O 1.1 字符流 1.1.1 什么是字符流 在Java中&#xff0c;字符流是指提供了基于字符的I/O能力的API。 Java 1.0中提供的基于字节的I/O流API只能支持8位字节流&#xff0c;无法妥善地处理16位Unicode字符。由于需要支持Unicode处理国际化字符&#xff0c;因此Java 1.…

TCP/IP详解——HTTPS 协议

文章目录 1. HTTPS 协议1.1 HTTPS 原理1.2 HTTPS 过程1.3 从数据包角度看 HTTPS 交互过程1.4 常见的 HTTPS 数据包解码1.4.1 ClientHello 数据包1.4.2 ServerHello 数据包 1.5 思考 1. HTTPS 协议 1.1 HTTPS 原理 HTTPS概念 HTTPS 是以安全为目标的HTTP通道&#xff0c;并不…

小 cookie,大作用:探索网站中的隐私追踪器(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

持续集成交付CICD:Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前端应用的蓝绿发布

目录 一、实验 1.蓝绿发布准备 2.Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前端应用的蓝绿发布 二、问题 1.手动构建Jenkins前端项目CI流水线报错 2.如何优化手动构建流水线选项参数 一、实验 1.蓝绿发布准备 &#xff08;1&#xff09;环境 表1 蓝绿发布…

flume:Ncat: Connection refused.

一&#xff1a;nc -lk 44444 和 nc localhost 44444区别 nc -lk 44444 和 nc localhost 44444 是使用 nc 命令进行网络通信时的两种不同方式。 1. nc -lk 44444&#xff1a; - 这个命令表示在本地监听指定端口&#xff08;44444&#xff09;并接受传入的连接。 - -l 选项…

前端视角看 Docker : 基础命令全面指南

引言 Docker是一种开源的容器化平台&#xff0c;它允许开发者将应用程序和其依赖打包在一个轻量级的、可移植的容器中。这使得应用程序在不同的环境中部署变得简单且高效。本文将介绍Docker的一些基础命令和概念&#xff0c;帮助初学者快速上手。 1. Docker简介 Docker使用…

054:vue工具 --- BASE64加密解密互相转换

第054个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

云原生之深入解析使用Telepresence轻松在本地调试和开发Kubernetes应用程序

一、 准备 telepresence 下载&#xff1a;https://www.telepresence.io/docs/latest/install/kubectl 下载&#xff1a;https://kubernetes.io/docs/tasks/tools/ 二、版本检测 $telepresence version Client: v2.5.3 (api v3) Root Daemon: not running User Daemon: not r…

css文本样式的使用

在CSS中&#xff0c;可以通过以下属性来设置文本的样式&#xff1a; color&#xff1a;设置文本的颜色。 p {color: red; }效果图&#xff1a; font-size&#xff1a;设置文本的字体大小。 p {font-size: 16px; }效果图&#xff1a; font-family&#xff1a;设置文本的字…

uniGUI学习之UniHTMLMemo1富文本编辑器

1]系统自带的富文本编辑器 2]jQueryBootstarp富文本编辑器插件summernote.js 1]系统自带的富文本编辑器 1、末尾增加<p> 2、增加字体 3、解决滚屏问题 4、输入长度限制问题 5、显示 并 编辑 HTML源代码(主要是图片处理) 1、末尾增加<p> UniHTMLMemo1.Lines…

【星环云课堂大数据实验】kafka消息发布与订阅

文章目录 一、Kafka概述二、实验环境三、实验准备四、实验目的五、实验步骤5.1、创建Kafka Topic5.2、Kafka消息发布5.3、Kafka消息订阅 六、实验感悟 一、Kafka概述 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。该项目的目标是为处理实…

持续集成交付CICD:Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前后端应用

目录 一、实验 1.部署Ansible自动化运维工具 2.K8S 节点安装nginx 3.Jenkins使用GitLab共享库实现基于Ansible的CD流水线部署前后端应用 二、问题 1.ansible安装报错 2.ansible远程ping失败 3. Jenkins流水线通过ansible命令直接ping多台机器的网络状态报错 一、实验 …