算法学习day28

news2024/11/15 8:33:41

一、寻找右区间(二分法)

题意:题目很容易理解 但是转换为二分法有点晦涩

给你一个区间数组 intervals ,其中 intervals[i] = [starti, endi] ,且每个 starti 都 不同 。区间 i 的 右侧区间 可以记作区间 j ,并满足 startj >= endi ,且 startj 最小化 。注意 i 可能等于 j 。

让我们去找一个区间的最小右侧区间,满足条件:右侧区间的start值>该区间的end值,可能存在多个右侧区间,取最小的start值的区间。并把该区间的下标放到数组里面返回。没有记作-

思路:

题目要让我们去找一个区间的最小右侧区间。如何做到最小?

1.首先将每个区间的start值进行排序

2.然后for循环遍历每一个区间,将其end值作为target。

3.然后二分查找第一个>=target的start值的下标。放到res数组中

4.返回res数组

代码:
//将每一个区间的左端点排序 遍历每一个数组的右端点 找到第一个比它大的左端点 然后加入到集合中。如何将左端点排序,使用二维数组
class Solution {
    public int[] findRightInterval(int[][] intervals) {
        //第一步 创建sortStart数组 并且排序
        int[][] sortStart=new int[intervals.length][2];//start值:下标
        for(int i=0;i<intervals.length;i++){
            sortStart[i]=new int[]{intervals[i][0],i};
        }
        Arrays.sort(sortStart,(a,b)->a[0]-b[0]);

        //第二步 遍历每一个区间的end 并且将其作为target
        int[] res=new int[intervals.length];
        for(int i=0;i<intervals.length;i++){
            int target=intervals[i][1];//第i个区间的end值
            int left=0,right=sortStart.length-1;
            //第三步 二分查找最小的>=end的start值的下标 放入res数组中
            while(left<right){
                int mid=left+(right-left)/2;
                if(sortStart[mid][0]>=target){
                    right=mid;
                }else{
                    left=mid+1;
                }
            }
            res[i]=sortStart[left][0]>=intervals[i][1]?sortStart[left][1]:-1;
        }
        return res;
    }
}

二、最长递增子序列(dp+二分法)

解法一:dp动态规划
dp五部曲:

1.dp[i]的含义:以i为结尾的最长递增子序列的长度

2.递推公式:dp[i]=Math.max(dp[i],dp[j]+1);在0->i,如果找到比nums[i]小的数,说明找到了一个递增序列,然后对dp[i]进行更新。

3.初始化dp[i]=1;

4.遍历的顺序:双层for循环,i从1开始,j从0开始直j<i;

代码:
class Solution {
    public int lengthOfLIS(int[] nums) {
        //1.dp[i]:以i结尾的最长递增子序列的长度
        int[] dp=new int[nums.length];
        Arrays.fill(dp,1);
        int max=1;
        for(int i=1;i<nums.length;i++){
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            max=Math.max(max,dp[i]);
        }
        return max;
    }
}
解法二:辅助数组+二分法
思路:

遍历数组,如果遇到的数字是大于尾部元素的,直接放到尾部元素后面,使得递增序列变大;

如果遇到的数字是小于尾部元素的,为了使递增的速度最慢,就要将该元素放到最合适的地方.

那么这个最合适的地方如何去寻找? 当然我们可以利用辅助数组单调递增的规律,使用二分查找法去寻找最合适的地方。

代码:
class Solution {
    public int lengthOfLIS(int[] nums) {
        //1.定义辅助数组
        int[] sortNums=new int[nums.length];
        int size=1;
        sortNums[0]=nums[0];
        for(int i=0;i<nums.length;i++){
            //如果大于的话 直接放到sortNums的后边
            if(nums[i]>sortNums[size-1]){
                sortNums[size++]=nums[i];
            }else{
                //如果小于的话 我们就要去找合适的位置
                int target=nums[i];
                int left=0,right=size-1;
                while(left<=right){
                    int mid=left+(right-left)/2;
                    if(sortNums[mid]>=target){
                        right=mid-1;
                    }else{
                        left=mid+1;
                    }
                }
                sortNums[right+1]=nums[i];
            }
        }
        return size;
    }
}

三、寻找峰值

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。你可以假设 nums[-1] = nums[n] = -∞ 。

因为nums[-1]=nums[n]=-∞;因此可以推断出:

nums[i]<nums[i+1]:那么后面一定存在一个峰值;

nums[i]>=nums[i+1];那么前面一定存在一个峰值;

思路:

那么就根据推断出的规律来进行二分查找:如果nums[mid]>=nums[mid+1] left=mid+1;

nums[mid]<nums[mid+1] right=mid;

代码:
class Solution {
    public int findPeakElement(int[] nums) {
        if(nums.length==1)return 0;
        int left=0,right=nums.length-1;
        while(left<right){
            int mid=left+(right-left)/2;
            if(nums[mid]>=nums[mid+1]){
                right=mid;
            }else{
                left=mid+1;
            }
        }
        return left;
    }
}

四、找到k个最接近的元素

题意:

给定一个 排序好 的数组 arr ,两个整数 k 和 x ,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。

输入:arr = [1,2,3,4,5], x = 3, k = 4
输出:[1,2,3,4] 相同距离的话 加入较小的
解法一:双指针删除法
思路:

逆向思维,在数组中删除(size-k)个离x最远的数字。可以使用双指针(left,right)向x靠近的同时不断删除元素。

比如在1 2 3 4 5中寻找4个最靠近3的数。

left指向1,right指向5。和3的距离都为2,将5删除。剩余四个数字 4==k。那么加入到集合中返回。

代码:
class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        // 双指针删除法
        List<Integer> res = new ArrayList<>();
        int number = arr.length;
        int left = 0;
        int right = number - 1;
        int count = 0;
        while (left <= right) {
            if (count == number - k) {
                for (int i = left; i <= right; i++) {
                    res.add(arr[i]);
                }
            }
            int diff1 = Math.abs(x - arr[left]);
            int diff2 = Math.abs(arr[right] - x);
            if (diff1 <= diff2)
                right--;
            else
                left++;
            count++;// 已经删除的数字
        }
        return res;
    }
}

五、俄罗斯套娃问题(最长上升子序列的二维版)

你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。

解法一:动态规划(超时)
解法二:贪心+二分查找
思路:

当只有宽和高都比上一个信封大的时候,才能实现套娃。

我们可以先对宽度进行升序排列,确认了宽度之后,就变成一维的求最长上升子序列了

如何求最大长度的高度序列。使用贪心算法:每次我们都希望放在末尾的值是一个符合条件并且尽可能小的数字,这样我们的长度最长的概率就最大。(因此当我们遇到heights[i]<tails[size]的时候,我们需要将heights[i]放到合适的位置,但是heights所对应宽度是可能相等的。在相等的时候,我们应该按照降序排列,这样在更新下一个的时候就会把它刷掉)。

举个例子理解一下:假如按照升序排列后为:(5,5),(6,6),(7,2)(7,1)(8,3),(9,4)。

1.envelopes[i][1]=5, 集合为空,tails[0]=5 size=1;

2.envelopes[i][1]=6,  6大于5,tails[1]=6,size=2;

3.envelopes[i][1]=2,  2小于6,给2寻找合适的位置,2<5。因此tails[0]=2;

4.envelopes[i][1]=1, 1小于6,给1寻找合适的位置,1<2,因此tails[0]=1;

5.envelopes[i][1]=3,3小于6,给3寻找合适的位置,3<6,因此tails[1]=3;

6.envelopes[i][1]=4,4大于3,因此tails[2]=4;size=3

寻找合适的位置是根据二分搜索法进行寻找。

代码:
class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        if(envelopes.length==0)return 0;;
        // 对信封的宽度排序
        Arrays.sort(envelopes,new Comparator<int[]>(){
            public int compare(int[] a,int[] b){
                return a[0]==b[0]?b[1]-a[1]:a[0]-b[0];
            }
        });
        int maxSize=1;
        int size=1;
        int[] tails=new int[envelopes.length];
        tails[0]=envelopes[0][1];
        for(int i=1;i<envelopes.length;i++){
            if(envelopes[i][1]>tails[size-1]){
                tails[size++]=envelopes[i][1];
            }else{
                int left=0,right=size-1;
                int target=envelopes[i][1];
                while(left<=right){
                    int mid=left+(right-left)/2;
                    if(tails[mid]>=target)right=mid-1;
                    else left=mid+1;
                }
                tails[right+1]=target;
            }
        }
        return size;
    }
}

六、寻找旋转排序数组中的最小值(要求时间复杂度为O(logN))

思路:二分法

旋转之前,是一个升序数组;旋转之后,会变成两段升序数组。

旋转时,小数往后走,大数旋转到前面来。分情况:

1.起始点在到达中间之前,(右边部分一定处于升序),nums[mid]的值一定是小于nums[right]的,此时起始点一定在mid左边(包括mid)

2.起始点到达中间之后大数占领起始点之前的区域,nums[mid]的值一定是大于nums[right]的,此时起始点一定是在右半部分的。(反证一下,如果起始点在左部分的话,那么起始点的右边一定是升序的,那么nums[mid]一定小于nums[right])

所以

1.当nums[mid]>nums[right],起始点一定在右边,更新左边界left=mid+1;

2.当nums[mid]<=nums[right],起始点一定在左边,更新有边界right=mid;

代码:
class Solution {
    public int findMin(int[] nums) {
        int size=nums.length;
        int left=0,right=size-1;
        while(left<right){
            int mid=left+(right-left)/2;
            if(nums[mid]<nums[right]){
                right=mid;
            }else{
                left=mid+1;
            }
        }
        return nums[left];
    }
}

七、搜索旋转排序数组

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

思路:

将一个排序好的数组旋转,变成两段升序序列(也可能是一段 就是原始的)。然后找到元素的起始点。两种情况:

1.如果起始点==0,说明并没有进行旋转,还是原始的数组。

2.如果起始点>0,说明进行了旋转,在两段序列中进行查找target就可以。

代码:
class Solution {
    public int search(int[] nums, int target) {
        if (nums.length == 1 && nums[0] == target)
            return 0;
        // 首先找到起始点 找到起始点之后 两段升序找target
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] <= nums[right]) {
                // 说明mid一定在左半边
                right = mid;
            } else {
                left = mid + 1;
            }
        }

        // left就是起始的位置
        int res1 = 0;
        int res2 = 0;
        if (left == 0)
            return binarySearch(nums, 0, nums.length - 1, target);
        else {
            res1 = binarySearch(nums, 0, left - 1, target);
            res2 = binarySearch(nums, left, nums.length - 1, target);
        }
        return res1 == -1 ? res2 : res1;
    }

    public int binarySearch(int[] nums, int left, int right, int target) {
        int index = -1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return nums[left]==target?left:index;
    }
}

八、搜索二维矩阵II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

思路:

如果从左上角或者右下角开始寻找的话,不具有区别度。

如果从右上角或者左下角寻找的话:

1.右上角:左边变小,下边变大

2.左下角:上边变小,右边变大

代码:
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        //从右上角出发
        int rol=matrix.length;
        int cow=matrix[0].length;
        int x=0,y=cow-1;
        while(x<rol&&y>=0){
            if(matrix[x][y]<target){
                x++;
            }else if(matrix[x][y]>target){
                y--;
            }else{
                return true;
            }
        }
        return false;
    }
}

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

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

相关文章

gptpdf深度解析:开源文档处理技术全攻略

目录 一、引言二、gptpdf 是什么&#xff1f;三、gptpdf 的功能特性1. 精准的 PDF 元素解析能力2. 对复杂文档结构的处理示例3. 高效的处理速度4. 低成本的优势 四、gptpdf 应用场景1. 学术研究与文献处理2. 企业文档管理3. 软件开发中的文档转换 五、gptpdf 代码示例1. 基本的…

【Vue3】组件通信之v-model

【Vue3】组件通信之v-model 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的…

图像梯度与几种算子

“滤波器”也可以称为“卷积核”&#xff0c;“掩膜”&#xff0c;“算子”等。 1、Sobel算子 Sobel算子是一个33的卷积核&#xff0c;利用局部差分寻找边缘&#xff0c;计算得到梯度的近似值。x和y方向的Sobel算子分别为&#xff1a; 梯度有方向&#xff0c;对于一个图像&a…

电子元器件—三极管(一篇文章搞懂电路中的三极管)(笔记)(面试考试必备知识点)

三极管的定义及工作原理 1. 定义 三极管&#xff08;Transistor&#xff09;是一种具有三层半导体材料&#xff08;P-N-P 或 N-P-N&#xff09;构成的半导体器件&#xff0c;用于信号放大、开关控制和信号调制等应用。三极管有三个引脚&#xff1a;发射极&#xff08;Emitter…

SpringBoot智慧旅游在线平台的设计与实现(源码+论文+部署讲解等)

博主介绍&#xff1a;✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HLM…

从地铁客流讲开来:地铁客运量特征

1.数据来源 数据来源&#xff1a;MetroWatch地铁观察 | 地铁客流量数据 在做城市地铁客流数据的整理及可视化这块其实国内已经有很多大牛一直在做无偿免费的更新&#xff0c;其中覆盖多城市且每日更新数据的主要有两个&#xff1a;一个是地铁数据库 | 地铁客流量查询 (metrod…

数字农业农村云平台整体规划建设方案PPT

数字农业农村云平台的规划建设方案是一个全面而复杂的项目&#xff0c;涉及到多个方面的整合与创新。根据搜索结果&#xff0c;以下是一些关键点&#xff1a; 资料下载方式&#xff0c;请看每张图片右下角信息 1. 组织领导与政策支持&#xff1a;加强组织领导&#xff0c;确保…

如何在联络中心使用人工智能驱动的语音分析?

人工智能驱动的语音分析是一种使用自然语言处理和机器学习技术的语音识别软件。借助呼叫中心的语音分析&#xff0c;您可以将实时语音转换为文本。之后&#xff0c;程序会评估此文本以揭示有关客户需求、偏好和情绪的详细信息。 在联络中心&#xff0c;语音分析工具有助于&…

OpenCV函数

1&#xff0c;cv2.imread cv2.imread:这个函数可以直接用cv2.imread(filename, cv2.IMREAD_GRAYSCALE)直接将图片以黑白图像输入&#xff0c;也可以通过cv2.imread(img, 0)来将图片以黑白图像输入。其实这两者是一样的&#xff0c;如下图所示&#xff0c;可以将特定的颜色通道…

C语言程序设计之结构体篇2

程序设计之结构体2 问题2_1的代码2_1结果2_1 问题1_2代码1_2结果1_2 问题1_3代码1_3结果1_3 问题1_4代码1_4结果1_4 问题2_1的 函数 f u n fun fun 的功能是&#xff1a; 对 N N N 名学生的学习成绩&#xff0c;按从高到低的顺序找出前 m m m &#xff08; m < 10 m<1…

『 C++11 』模板可变参数包,Lambda表达式与 function 包装器

文章目录 模板可变参数模板可变参数包的展开可变参数包与STL容器中的emplace函数关系 Lambda 表达式function 包装器function 包装器对成员函数的包装bind 绑定 模板可变参数模板 可变参数模板是C11引入的一个特性,允许模板接收任意数量的参数; 该特性增加了C的泛型编程能力; 可…

搭建jenkins一键部署java项目

一、搭建jenkins 链接: https://pan.baidu.com/s/1jzx15PiyI8EhLd_vg7q8bw 提取码: ydhl 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 直接使用docker导入镜像&#xff0c;运行就好 docker run -di --name jenkins -p 8080:8080 -v /home/jenkins_home:/var/je…

黑神话:悟空

《黑神话&#xff1a;悟空》是由游戏科学公司制作的以中国神话为背景的动作角色扮演游戏&#xff0c;将于2024年8月20日发售 [9] [14]&#xff0c;简体中文PC标准版售价268人民币,数字豪华版售价328人民币。 [27] [34] 游戏中&#xff0c;玩家将扮演一位“天命人”&#xff0c…

洗袜子的小洗衣机哪款好?小户型洗衣机推荐!懒人洗袜子神器分享

市面上的那些迷你的小型洗衣机可以洗袜子&#xff0c;洗涤空间够一次性洗5-6双左右的袜子&#xff01;这种不仅不会因为清洗的衣物数量少而浪费水浪费电&#xff0c;同时使用也很便利&#xff0c;小小个的放在家的任意角落就可以进行清洗&#xff0c;不仅是清洗袜子这些&#x…

jquery.ajax + antd.Upload.customRequest文件上传进度

前情提要&#xff1a;大文件分片上传&#xff0c;需要利用Upload的customRequest属性自定义上传方法。也就是无法通过给Upload的action属性赋值上传地址进行上传&#xff0c;所以Upload组件自带的上传进度条&#xff0c;也没法直接用了&#xff0c;需要在customRequest中加工一…

GraphSAGE (SAmple and aggreGatE)知识总结

1.前置知识 inductive和transductive 模型训练&#xff1a; Transductive learning在训练过程中已经用到测试集数据&#xff08;不带标签&#xff09;中的信息&#xff0c;而Inductive learning仅仅只用到训练集中数据的信息。 模型预测&#xff1a; Transductive learning只能…

6.前端怎么做一个验证码和JWT,使用mockjs模拟后端

流程图 创建一个发起请求 创建一个方法 getCaptchaImg() {this.$axios.get(/captcha).then(res > {console.log(res);this.loginForm.token res.data.data.tokenthis.captchaImg res.data.data.captchaImgconsole.log(this.captchaImg)})}, captchaImg: "", 创…

【数据结构】排序基本概念、插入排序、希尔排序(详解)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

java学习--泛型

前言 当我们将dog类放入集合List中想要遍历通过一下手段可实现遍历名字和年龄&#xff0c;但是当我们要加入一个新的Cat类时&#xff0c;他并不会报错&#xff0c;只有编译了才会报错&#xff0c;因为在这一步的时候注定了只能是Dog类&#xff0c;但这是非常不方便的 此时我们…

哦吼,新模型?文生图领域的新模型FLUX.1(附模型下载网盘地址和详细使用方法)

&#x1f3a1;背景 Black Forest Labs 是由 Stable Diffusion 原班人马成立的公司&#xff0c;致力于研发优质的多模态模型并开源。该公司由多位前 Stability AI 研究员组成&#xff0c;包括 Robin Rombach 在内的团队成员&#xff0c;他们在图像和视频生成领域有着杰出的贡献…