算法复习(上)

news2024/9/22 11:32:45

数组复习

数组复习基本就是熟练使用数组,经常配合指针使用以及思维的使用

443. 压缩字符串 - 力扣(LeetCode)

使用双指针分别标志我们在字符串中读和写的位置,当读指针 read 位于字符串的末尾,或读指针 read 指向的字符不同于下一个字符时,我们就认为读指针 read 位于某一段连续相同子串的最右侧。该子串对应的字符即为读指针 read 指向的字符串。我们使用变量 left

class Solution {
     public static int compress(char[] chars) {
        int n = chars.length;
        if(n == 1)return 1;
        char b [] = new char[n];
        for (int i = 0,j = 0; i < n;) {
            b[j] = chars[i];i++;j++;
            int sum = 1;
            while (i < n && chars[i] == chars[i-1]){
                i++;
                sum++;
            }
            if(sum != 1){
                for (int k = 0; k < Integer.toString(sum).length(); k++) {
                    b[j] = Integer.toString(sum).charAt(k);j++;
                }
            }
        }
        int m = 0;
        for (int i = 0; i < n; i++) {
            if(b[i] != 0){
                 chars[i] = b[i];
//                System.out.println(b[i]);
                m++;
            }
        }

        return m;
    }
}

双指针复习

1679. K 和数对的最大数目 - 力扣(LeetCode)

很easy,用两个指针从最左边和最右边,逐渐靠拢,符合条件就筛选。

class Solution {
    public static int maxOperations(int[] nums, int k) {
        Arrays.sort(nums);
        int n = nums.length;
        int res = 0,i = 0,j = n-1;
            while (i<j){
                if(nums[i] + nums[j] == k){
                    i++;j--;res++;
                }else if (nums[i] + nums[j] > k){
                    j--;
                }else{
                    i++;
                }
            }
        return res;
    }
}

滑动窗口

3. 无重复字符的最长子串 - 力扣(LeetCode)

也是类似于双指针,这个双指针中间的比作一个窗口,然后两个指针都是从0开始走,然后符合条件就扩张右指针,如果符合左边的条件同样去移动左指针来拿到最佳窗口,经常和map一并使用。

76. 最小覆盖子串 - 力扣(LeetCode)

class Solution {
    public String minWindow(String s, String t) {
        //维护s中滑动窗口中各个字符出现次数
        Map<Character,Integer> hs = new HashMap<>();
        //维护t中滑动窗口中各个字符出现次数
        Map<Character,Integer> ht = new HashMap<>();

        for (int i = 0; i < t.length(); i++) {
            ht.put(t.charAt(i),ht.getOrDefault(t.charAt(i),0)+1);
        }
        String ans  = "";
        int len = Integer.MAX_VALUE;//这个len是为了让结果是存储的最短的字符串而存在的变量
        int cnt = 0;//cnt用来计数此时hs中复合ht中的数字此时有几个了
        for (int left = 0,right = 0; right < s.length(); right++) {
            hs.put(s.charAt(right),hs.getOrDefault(s.charAt(right),0) + 1);
            //如果ts表中也包含当前字符
            if(ht.containsKey(s.charAt(right))){
                //并且hs表中字符是 <= ht中字符的,说明该字符是必须的,并且还未到达字符串t所要求的数
                if(hs.get(s.charAt(right)) <= ht.get(s.charAt(right))){
                    cnt++;
                }
            }

            //收缩滑动窗口
            //如果左边的值不在ht表中,或者他在hs表中出现次数多于ht表中的出现次数
            while (left < right && (!ht.containsKey(s.charAt(left)) ||
                    hs.get(s.charAt(left)) > ht.get(s.charAt(left)))){
                hs.put(s.charAt(left),hs.get(s.charAt(left)) - 1);
                left++;
            }
            //此时滑动窗口包含字符串t的全部字符
            if(cnt == t.length() && right - left + 1 < len){
                len = right - left + 1;
                ans = s.substring(left,right+1);
            }
        }
        return ans;
    }
}

哈希复习

用一个数组模拟哈希的查找,关于hash的用法标记

1、把哈希数组的长度设置为1009.模也是1009,设置一个默认变量M:意思是空,用Arrays.fill(h,M)意思就是将所有的数组位置都传入值M,

2、在 for (Map.Entry<Integer,Integer> entry : map.entrySet()) {}

中意思就是:map.ectrySet()指的是把map中的所有键值对都设为一个集合,用来遍历,Map.Entry<Integer,Integer>意思是遍历的每一个对象的类型,entry就是每一次遍历出来的对象,该对象可以调用哈希的函数,如entry.getValue()\entry.getKey();等等

3、最后一个方法find 其中是使用模运算hash存储的查找过程,具体为

    • int k = (x % N + N) % N;: 这行代码计算x在哈希表中的初始索引k。先计算x % N,再通过加上N并对N取模来确保k是一个非负数(处理负数情况)。
    • while (h[k] != M && h[k] != x): 这是一个循环,它的条件是当前h[k]既不等于M(可能表示该位置未使用或是空槽),也不等于x(要查找的元素)。这个循环的目的是在哈希表中找到x
    • k++; if(k == N) k = 0;: 这两行代码用来处理探查。如果当前的位置不符合条件,就移动到下一个位置。如果k达到N,那么就循环回到数组的开头(设置为0),实现循环探查。
  1. 返回值:
    • return k;: 最后,函数返回找到x的位置索引。如果在哈希表中没有找到x,它返回的是该元素可能被放置的第一个空位置的索引,或者当前位置(如果h[k]x)。
public class 独一无二的出现次数 {
    //https://leetcode.cn/problems/unique-number-of-occurrences/?envType=study-plan-v2&envId=leetcode-75
    final int N = 1009,M = 2009;
    int h[] = new int[N];
    public boolean uniqueOccurrences(int[] arr){
        Map<Integer,Integer> map = new HashMap<>();
        Arrays.fill(h,M);
        for (int i = 0; i < arr.length; i++) {
            int k = find(arr[i]);
            if(h[k] == M){
                h[k] = arr[i];
                map.put(arr[i],1);
            }else{
                map.put(arr[i],map.get(arr[i])+1);
            }
        }
        Map<Integer,Integer> map2 = new HashMap();
        for (Map.Entry<Integer,Integer> entry : map.entrySet()) {
            int value = entry.getValue();
            if(map2.containsKey(value))return false;
            map2.put(value,1);
        }
        return true;
    }
    //模运算hash存储buckets,
    public int find(int x){
        int k = (x % N + N)% N;
        while (h[k] != M && h[k] != x){
            k++;if(k == N) k = 0;
        }
        return k;
    }
}

二叉树复习

二叉树总是带着递归一起使用,所以想要学好二叉树算法就一点要理解递归的奥秘

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

其实我已经给了大部分注释,看上面的多行注释意思为自定义了一个TreeNode的类,其中有左二叉树、右二叉树,和val值。

二叉树总是使用前中后序遍历,这里面用的是中序遍历,也就是中左右的顺序,拿出二叉树的所有值,然后递归返回。

它的要求就是找到p和q,但是不用区分是哪一个,而且题目说一定存在,所以中序遍历的中,就是只要找到值就return。

如果找不到然后找该节点的左子树用递归调用自己这个方法来找,左之后就是右,然后再往下就是三个判断,是用在递归返回的时候使用

如果左右子树都没找到p、q那就继续返回null,如果谁找到了就返回谁,如果两个都找到了就直接返回根节点,这样根节点就会直线return,直到

返回到栈中的最底层,然后返回函数,得出答案。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q){
            //只要当前根节点是p和q的中任意一个,就返回(因为无法比根节点更祖了)
            return root;
        }
        //根节点不是p和q中的任意一个,那么就继续分别去左子树和右子树找p和q
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        //p和q都没有找到,那就没有返回null
        if(left == null && right == null){
            return null;
        }
        //因为题目保证p和q均存在于给定的二叉树,所以一定是有结果不是左就是右。
        //左子树没有p也没有q,就返回右子树的结果
        if(left == null)return right;
        if(right == null)return left;

        //前面进行了判断,也就是左右都找到了值,则此时的root就是我们想要的值:二叉树的最近公共祖先。
        //左右子树都找到p和q了,那就说明p和q分别在两个子树上,所以次数的最近公共祖先就是root
        return root;

    }
}

437. 路径总和 III - 力扣(LeetCode)

这道题是在这里插入图片描述

能够顺便用到前缀和,同时也用到了递归,寻找多个节点的值为targetSum,那么如果某一个节点是preSum,所以他要找的就是preSum - targetSum (很重要) 如果此时在他上方有前缀和,map存储的是前缀和的数量,可以直接用count+= (那个数量)比如用10 5 3 这条路来看,map(10,1) map(15,1) map(18,1),当指针指到3的时候,此时preSum = 18,preSum - targetSum = 18 - 8 得10,相当于使用前缀和之差来找到自己想要的路径总合,最后还用到了回溯,回溯就是在递归之后将此次函数中将深搜之前更改的公共资源恢复原样。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int targetSum,count = 0;
    Map<Integer,Integer> map;//map中key存储前缀和的值,value存储在当前这条路径中此前缀和的出现的次数
    public int pathSum(TreeNode root, int targetSum) {
        if(root == null) return 0;
        this.targetSum = targetSum;
        this.map = new HashMap<>();
        map.put(0,1);//表示前缀和为0的节点为空,有一个空  也就是根节点的前缀和
        dfs(root,0);
        return count;

    }
    private void dfs(TreeNode node, int preSum) {
        if(node == null)return;
        preSum += node.val;//preSum保存的是前缀和的值,
        count += map.getOrDefault(preSum - targetSum,0);//累计满足要求的前缀和的数量
        map.put(preSum,map.getOrDefault(preSum,0) + 1);//然后将这条路的前缀和的值为preSum给存储。
        //进行深搜,递归
        dfs(node.left,preSum);
        dfs(node.right,preSum);
        map.put(preSum,map.get(preSum) - 1);//路径退缩(回溯),去掉不在路径上的当前结点的前缀和,必存在无需getDefault。
    }
}

前缀和/差分复习

差分就是把前缀和给反过来了,前缀和是原数组arr,前缀数组brr,其中brr中是

        for (int i = 1; i < arr.length; i++) {
            arr[i] = sc.nextInt();
            brr[i] = brr[i-1]+arr[i];
        }

而差分的话是,原数组是arr,然后差分数组是brr,通过每次在arr中的操作,比如在下标1-3的地方都进行+1,此时就在差分数组中brr[1] + 1,然后再brr[3]-1

等把所有的操作结束后,就把差分数组brr进行前缀和,此时的brr就是原数组进行多次操作后的样子,其中最重要的就是insert函数。同理的矩形差分数组也是这样。

797. 差分 - AcWing题库

import java.util.Scanner;

public class Main {
    static int arr [];
    static int brr [];
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        arr = new int[100010];
        brr = new int[100010];
        for (int i = 1; i <= n; i++) {
            arr[i] = sc.nextInt();
            insert(i,i,arr[i]);
        }
        int q [][] = new int[m][3];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < 3; j++) {
                q[i][j] = sc.nextInt();
            }
            insert(q[i][0],q[i][1],q[i][2]);
        }
        for (int i = 1; i <= n; i++) {
            brr[i] += brr[i-1];
        }
        for (int i = 1; i <= n; i++) {
            System.out.printf("%d ",brr[i]);
        }

    }
    private static void insert(int l,int r, int c){
        brr[l] +=c;
        brr[r + 1] -=c;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python3 第八十一课 -- urllib

目录 一. 前言 二. urllib.request 三. urllib.error 四. urllib.parse 五. urllib.robotparser 一. 前言 Python urllib 库用于操作网页 URL&#xff0c;并对网页的内容进行抓取处理。 本文主要介绍 Python3 的 urllib。 urllib 包 包含以下几个模块&#xff1a; url…

C# 利用自定义特性,动态拼接sql,查询数据库,动态新增datagridview 列

之前在给一个工厂客户开发一套“售后包装防错系统”的时候&#xff0c;由于业务比较复杂&#xff0c; 每个表的字段基本都保持在10-20个字段区间&#xff0c;如下截图&#xff08;可向右滑动滚动条&#xff09; 正常的做法&#xff0c;肯定是一顿卡卡操作&#xff0c;新建列&…

Java——反射(1/4):认识反射(反射(Reflection)、反射学什么 )、获取类(获取Class对象的三种方式、代码演示)

目录 认识反射 反射&#xff08;Reflection&#xff09; 反射学什么 获取类 获取Class对象的三种方式 代码演示 认识反射 反射&#xff08;Reflection&#xff09; 反射就是&#xff1a;加载类&#xff0c;并允许以编程的方式解剖类中的各种成分&#xff08;成员变量、…

基于BP神经网络的苦瓜生长含水量预测模型matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) T表示温度&#xff0c;v表示风速&#xff0c;h表示模型厚度 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#…

RocketMQ5.0消费者负载均衡实战-顺序消息负载均衡

在看本篇文章之前可以看一下非顺序消息消息负载均衡相关 RocketMQ5.0消费者负载均衡实战-CSDN博客 顺序消息消息粒度负载均衡 顺序消息的负载均衡主要是针对顺序消息特性进行负载&#xff0c;强遵循顺序消息的特性 RocketMQ 非顺序消息粒度负载均衡 RocketMQ 顺序消息粒度负载…

开发笔记:uniapp+vue+微信小程序 picker +后端 省市区三级联动

写在前面 未采用: 前端放置js 或者 json文件进行 省市区三级联动 采用&#xff1a; 前端组件 后端接口实现三级联动 原因&#xff1a;首先微信小程序有大小限制&#xff0c;能省则省&#xff0c;其次&#xff1a;方便后台维护省市区数据&#xff0c;完整省市区每年更新好像…

HR招聘测评,什么样的候选人无法通过面试

当下企业招聘普遍采用在线人才测评的方式&#xff0c;比传统的面试更加深入&#xff0c;也更加全面的掌握候选人的各方面信息&#xff0c;尤其是综合素质方面&#xff0c;是传统面试所无法比拟的。 在线人才测评的环节&#xff0c;往往安排在面试前&#xff0c;或者是在面试后…

Canvas:二次贝塞曲线

目录 1. 含义2. 方法说明3. 绘制对号4. 绘制聊天框 1. 含义 二次贝塞尔曲线是从起始点开始&#xff0c;通过控制点影响&#xff0c;最终到达终点的平滑曲线。 控制点虽然不在曲线上&#xff0c;但它决定了曲线的形状。 通过调整控制点的位置&#xff0c;可以改变曲线的弯曲方向…

苍穹外卖项目DAY02

苍穹外卖项目Day02 1、员工管理 1.1、新增员工 1.1.1、需求分析和设计 产品原型&#xff1a; 接口设计&#xff1a; 数据库设计&#xff08;employee表&#xff09;&#xff1a; 1.1.2、代码开发 根据新增员工接口设计对应的DTO&#xff1a; 注意&#xff1a;当前端提交的…

(javaweb)分层解耦

目录 一.三层架构 二.分层解耦 三.IOC&DI入门 四.IOC详解 五. DI详解 一.三层架构 复用性差&#xff0c;难以维护和管理 前端发起请求&#xff0c;先会到达controller&#xff0c;再调用service进行逻辑处理&#xff0c;逻辑处理的前提是先拿到数据&#xff0c;到dao…

模拟电子技术(上海交大 郑益慧)

概述 深入学习基础器件、然后基于基础器件做应用电路设计, 然后做放大电路设计,在这做多级放大电路 最后 构成了集成放大器 改善电路性能、让电路稳定,最终要的思想,就是引入反馈 如何学习 多练习、多实践增加感性(sumulink) 仿真 本征半导体与杂质半导体 二极管 单向导…

Java Facade 模式(外观模式)增强您的架构

通过我们的深入解释和实际示例揭示 Java Facade 模式的简单性 - 简化您的代码并增强您的架构。 您是否厌倦了让您头疼的乱七八糟的代码&#xff1f;您是否在为难以操作和维护的复杂软件而苦恼&#xff1f;那么让我们来谈谈外观 — — 不&#xff0c;不是建筑物的正面&#xff0…

Ceph分布式存储系统的搭建与使用

目录 一. 环境准备 二. 安装Docker 三. admin节点安装cephadm 四. admin节点给另外四个主机导入镜像 五. 向集群中添加节点 六. Ceph使用 列出可用设备 清除设备数据---针对有数据的设备 检查 OSD 状态 Ceph 集群中添加一个新的 OSD 查看集群的健康状态 指定MDS 列…

Javascript 基本引用类型

思维导图 Javascript基本引用类型思维导图 1:date的简单使用 let date new Date() // 获取当前的时间 年月日时分秒 获取时间 getTime() // 返回日期的毫秒表示&#xff1b;与 valueOf()相同 getFullYear() // 返回 4 位数年&#xff08;即 2019 而不是 19&#xff09; ge…

SD-WAN企业组网:与传统组网有何不同?

很多企业已经尝试过使用SD-WAN来进行组网。SD-WAN代表着一种新兴的网络连接技术&#xff0c;与传统的网络架构相比&#xff0c;它在许多方面都有明显的不同。 SD-WAN基于软件定义网络&#xff08;SDN&#xff09;的概念&#xff0c;提供集中化的网络控制和智能优化&#xff0c;…

软考:软件设计师 — 13.数据结构

十三. 数据结构 数据结构部分也可参考文章&#xff1a;Java数据结构知识点 — 5种常见数据结构 1. 线性结构 &#xff08;1&#xff09;线性表 顺序表 线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的数据元素&#xff0c;从而使得逻辑上相邻的两个元素…

仿RabbiteMq简易消息队列基础篇(future操作实现异步线程池)

TOC 介绍 std::future 是C11标准库中的一个模板类&#xff0c;他表示一个异步操作的结果&#xff0c;当我们在多线程编程中使用异步任务时&#xff0c;std::future可以帮助我们在需要的时候&#xff0c;获取任务的执行结果&#xff0c;std::future 的一个重要特性是能…

lvm知识终结

、什么是 LVM LVM 是 Linux 下对磁盘分区进行管理的一种工具&#xff0c;适合管理大存储设备&#xff0c;并允许用户动态调整文件系统的大小 lvm 常用的命令 功能 PV 管理命令 VG 管理命令 LV 管理命令 scan 扫描 pvscan vgscan lvscan create 创建 pvcreate v…

水仙花语:花中情诗,心灵低语

一、水仙花语的丰富内涵 水仙花的花语丰富多样&#xff0c;其中“纯洁”是其最为显著的象征之一。水仙花洁白无瑕的花瓣&#xff0c;宛如纯洁无暇的心灵&#xff0c;给人以清新、高雅之感。这种纯洁不仅体现在花朵的外观上&#xff0c;更蕴含着一种纯净、美好的精神内涵&#x…