java算法day2

  • 螺旋矩阵
  • 搜索插入位置
  • 查找元素第一个位置和最后一个位置

螺旋矩阵

请添加图片描述
请添加图片描述

解法:模拟,核心在于你怎么转,还有就是处理边界,边界如何收缩,什么时候停止旋转。最内圈的时候怎么处理。

通过上图的模拟来解决这个问题:
1.每条边都采取这种左闭又开来进行统一的处理。
2.分别设置四个边界,left = 0,right = matrix[0].length-1, top = 0,bottom = matrix.length-1。
3.每轮:从左上角开始转一圈,这个转圈就相当于遍历然后把结果加入进结果集。这里建议自己创建一个变量用于遍历,不要用前面预定义的量。每条边处理完就边界++。
4.什么时候停:从图中,最内部那里的处理显然就是已经停止转了。这里显然就不是循环的逻辑了,所以到内部这里就该停止。
5.这里就分两种情况,上面图里面画出来了。
第一种:属于长比宽高,这样转最终导致最后需要处理一行,此时上边界和下边界相等,而左边界和右边界不等。
第二种:属于宽比长高,这样转最终导致最后需要处理一列,此时左边界和右边界相等,而上边界和下边界不等。

所以就可以总结出啥时候停止转动,就是当上两种有其中一种情况出现时,就可以停止转了。
while(top<bottom && left<right) 。

这两种情况搞个if else 做判断处理这两种情况就行了。

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        int top = 0;
        int bottom = matrix.length-1;
        int left = 0;
        int right = matrix[0].length-1;

        List<Integer> list = new ArrayList<>();

        while(top<bottom && left<right){
            for(int i = left;i<right;i++)list.add(matrix[top][i]);
            for(int i = top;i<bottom;i++)list.add(matrix[i][right]);
            for(int i = right;i>left;i--)list.add(matrix[bottom][i]);
            for(int i = bottom;i>top;i--)list.add(matrix[i][left]);
            ++left;
            --right;
            ++top;
            --bottom;
        }

        if(bottom == top){
            for(int i = left;i<=right;i++){
                list.add(matrix[top][i]);
            }
        }else if(left == right){
            for(int i = top;i<=bottom;i++){
                list.add(matrix[i][top]);
            }
        }

        return list;

        
    }
}

搜索插入位置

注意这个题有一个很大的前提:自增不重复。
由于是有序自增不重复,涉及搜索,最快的肯定就是二分查找。所以直接写二分。
写法1:二分,左闭右闭写法。
这种处理,即left = 0。right = nums.length-1。每次处理,两侧都是封闭的区间。
所以while的条件是left<=right。可以取等,因为两侧闭区间,当相遇的时候是有意义的。
从模拟的结果来说
这里还有一个剪枝的优化。nums[mid] == target这个位置可以停,因为当相等的时候,插前面后面都一样。
否则最终插入位置都会在left停止的位置。(自己模拟一下)。

如果左闭右闭,你不进行+1或者-1操作,会发生死循环。看下面这个图的这个例子就懂了

请添加图片描述

快速记忆:二分,左闭右闭,mid剪枝早停,最终left。

请添加图片描述

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0; //这个就是左闭右的写法,那就是维护的区间初始化就是左右两边。
        int right = nums.length-1;  //封闭区间
        while(left<=right){  //由于是封闭区间,所以是有可能左边等于右边的,因为封闭所以他们的格子是有意义的
            int mid = left + (right-left)/2;   //计算中点
            if (nums[mid] == target){   
                return mid;  //技巧剪枝判断早停
            }else if(nums[mid]>target){
                right = mid - 1; //这是封闭区间的处理,就是由于那个格子有意义,所以才进行-1.下面的left的处理是同理。
            }else{
                left = mid +1 ;
            }
        }
        return left;  //自己模拟,最后就是在left停下
    }
}

查找元素第一个位置和最后一个位置

解法仍然是二分

**这个题仍然是递增,但是和上一题的区别就在于这个题有重复数字。上一题是没有的。**这个题如果还用上面那个题的思维,那么会出现一个情况,虽然插入后得到的数组看上去没问题,但实际上这个插入位置不一定是题目想要的第一个位置和最后一个位置。看下面这个图。
请添加图片描述
用上一个题的思路二分下来就会有这样的问题,这个指向的位置,不一定是边界。

怎么办:思路:
这里我肯定是想继续往左边找,想办法找到左边界。我们只需改在

target <= nums[mid] 时,让 right = mid - 1即可,这样我们就可以继续在 mid 的左区间继续找 5 。

怎么理解这个操作:
本来是target<nums[mid]。这里变<=。当target<nums[mid]时,就是要往左区间去找目标。而这里取一个等,就是说明这里是有可能有target = nums[mid]的可能性。但是这个mid不一定是最左,所以此时改变右区间的时候,右边界往左边再移动一格(往左边继续搜)。

这里可能有一个问题,万一这个就是左边界怎么办?
继续往后推导,最后left会停在结果的位置。所以不用担心。
直接上推导图。请添加图片描述
请添加图片描述

找上边界就是反过来。逻辑同理,想办法往右边搜,那就是target>=nums[mid],即左边有可能会摸到,但是我还是要往右边挪动一格,那就left = mid + 1.最后right会停在最终的最后一个元素。

现在就直接写两个函数调用就完事了。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = leftIndex(nums,target);
        int right = rightIndex(nums,target);
        // if(nums[left] != target || nums[right] != target){
        //     return new int[]{-1,-1};
        // } 
        if(left > right ){
            return new int[]{-1,-1};
        }
        return new int[]{left,right};
    }

    public static int leftIndex(int[] nums,int target){
        int left = 0;
        int right = nums.length - 1;
        while(left<=right){
            int mid = left + (right - left)/2;
            if(target<=nums[mid]){
                right = mid -1;
            }else{
                left = mid + 1;
            }
        }

        return left;
    }

    public static int rightIndex(int[] nums,int target){
        int left = 0;
        int right = nums.length - 1;
        while(left<=right){
            int mid = left + (right-left)/2;
            if (target>=nums[mid]){
                left = mid + 1;
            }else{
                right = mid -1;
            }
        }

        return right;
        
    }
}

要注意的细节:
1.关于target<=nums[mid]和target>=nums[mid],为什么找左边界要用前者,右边界要用后者。这里用反了就做不出来?
前者:可以发现找左边界的原理是不断的压缩左边维护的区间,所以target<=nums[mid]就可以使得维护的重复右边界往左边收缩。
后者:右边界的原理就是不断的压缩右边维护的区间,所以target<=nums[mid]就可以使得维护的重复左边界往左边收缩。

2.特判

// if(nums[left] != target || nums[right] != target){
        //     return new int[]{-1,-1};
        // } 

这里千万不能这么写。必须这样写:

if(left > right ){
            return new int[]{-1,-1};
        }

这个例子用于判断当target不在数组中。
先说结果:
当leftIndex执行结束后,left变量指向的是第一个大于target元素的位置,或者是数组长度(如果target大于所有元素)。
当rightIndex指向结束后,right指向的是最后一个小于target的元素的位置,或者是-1(如果target小于所有元素)

看模拟就懂了
nums = [5, 7, 7, 8, 8, 10],目标值 target = 6。
执行 leftIndex:
初始:left = 0, right = 5
第一次循环:mid = 2 (nums[2] = 7), 7 > 6 -> right = 1
第二次循环:mid = 0 (nums[0] = 5), 5 < 6 -> left = 1
第三次循环:mid = 1 (nums[1] = 7), 7 > 6 -> right = 0
循环结束:left = 1
可以看出最后left停留在第一个大于target元素的位置。

执行 rightIndex:
初始:left = 0, right = 5
第一次循环:mid = 2 (nums[2] = 7), 7 > 6 -> right = 1
第二次循环:mid = 0 (nums[0] = 5), 5 < 6 -> left = 1
第三次循环:mid = 1 (nums[1] = 7), 7 > 6 -> right = 0
循环结束:right = 0
可以看出最后left停留在第一个小于target元素的位置。

综上,在这个例子中,最后那肯定left>right了。

3.有可能会想到这个例子nums = []。
这个也包含在里面处理了,这样一开始就left>right了。最终left = 0,right = -1.所以也满足left>right.

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

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

相关文章

数据库锁等待排查方法、命令行安装数据库及授权文件更新

欢迎关注“数据库运维之道”公众号&#xff0c;一起学习数据库技术! 本期将为大家分享“数据库锁等待排查方法、命令行安装数据库及授权文件更新”的运维技能。 关键词&#xff1a;锁等待、V$LOCK、V$TRXWAIT、死锁、锁超时、命令行部署达梦、授权文件更新 当用户反馈执行SQL语…

1985-2022年各地级市专利申请数据

1985-2022年各地级市专利申请数据 1、时间&#xff1a;1985-2022年 2、指标&#xff1a;行政区划代码、地区、省份、城市、年份、发明公布&#xff08;申请数&#xff09;、其中&#xff1a;获得授权、外观设计申请量、实用新型申请量 3、来源&#xff1a;国家知识产权局 4…

【Java】简单实现图书管理系统

前言 在本篇博客当中&#xff0c;我们会使用Java基础语法来简单实现一个图书管理系统&#xff0c;主要用到的知识为&#xff1a;封装、多态、继承、接口等等&#xff0c;并不会使用数据库来存储数据&#xff0c;请注意 需求 1. 要求设置管理员和普通用户两种身份&#xff0c…

【深度学习实战(9)】三种保存和加载模型的方式

一、state_dict方式&#xff08;推荐&#xff09; torch.save(model.state_dict(), PATH)model YourModel() model.load_state_dict(torch.load(PATH)) model.eval()记住一定要使用model.eval()来固定dropout和归一化层&#xff0c;否则每次推理会生成不同的结果。 二、整个…

实验室三大常用仪器3---交流毫伏表的使用方法(笔记)

目录 函数信号发生器、示波器、交流毫伏表如果连接 交流毫伏表的使用方法 测量值的读数问题 实验室三大常用仪器1---示波器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客 实验室三大常用仪器2---函数信号发生器的基本使用方法&#xff08;笔记&#xff09;-CSDN博客…

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…

Git常见命令行操作和IDEA图形化界面操作

设置Git用户名和标签 在安装完Git以后需要设置用户和签名&#xff0c;至于为什么要设置用户签名可以看一下这篇文章【学了就忘】Git基础 — 11.配置Git用户签名说明 - 简书 (jianshu.com) 基本语法&#xff1a; git config --global user.name 用户名 git config --global u…

SpringBoot项目创建及简单使用

目录 一.SpringBoot项目 1.1SpringBoot的介绍 1.2SpringBoot优点 二.SpringBoot项目的创建 三.注意点 一.SpringBoot项目 1.1SpringBoot的介绍 Spring是为了简化Java程序而开发的&#xff0c;那么SpringBoot则是为了简化Spring程序的。 Spring 框架&#xff1a; Spring…

ARM之栈与方法

ARM之栈与方法 计算机中的栈是一种线性表&#xff0c;它被限定只能在一端进行插入和删除操作&#xff08;先进后出&#xff09;。通常将可以插入和删除操作的一端称为栈顶&#xff0c;相对的一端为栈底。 通常栈有递增堆栈&#xff08;向高地址方向生长&#xff09;、递减堆栈…

鸿蒙OpenHarmony【搭建Ubuntu环境】

搭建Ubuntu环境 在嵌入式开发中&#xff0c;很多开发者习惯于使用Windows进行代码的编辑&#xff0c;比如使用Windows的Visual Studio Code进行OpenHarmony代码的开发。但当前阶段&#xff0c;大部分的开发板源码还不支持在Windows环境下进行编译&#xff0c;如Hi3861、Hi3516…

Day37 IO流的操作

Day37 IO流的操作 文章目录 Day37 IO流的操作Java的文件拷贝利用 文件字节输出流 向文件写入数据利用 文件字节输入流 读取文件里的数据利用 带缓冲区的字节输出流 向文件写入数据利用 带有缓冲区的字节输入流 读取文件里的数据利用 字符输出转换流 向文件写入数据利用 字符输入…

Java全套智慧校园系统源码springboot+elmentui +Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术

Java全套智慧校园系统源码springbootelmentui Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术 智慧校园指的是以物联网为基础的智慧化的校园工作、学习和生活一体化环境&#xff0c;这个一体化环境以各种应用服务系统为载体&#xff0c;将教学、科研、管理和校园…

豆瓣影评信息爬取 (爬虫)

代码块&#xff1a; from lxml import etree import requestsheaders{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0 }url_list[] for i in range(0,5):i*20urlsf"https:…

day02-新增员工

day01 新增员工业务逻辑整理 EmployeeController.java PostMappingApiOperation("新增员工")public Result save(RequestBody EmployeeDTO employeeDTO){System.out.println("当前线程的ID:" Thread.currentThread().getId());log.info("新增员工&a…

[leetcode] 56. 合并区间

文章目录 题目描述解题方法排序java代码复杂度分析 题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区…

UWB人员定位系统适用的场景有哪些?​​​​​​​10厘米工业级实时轨迹高精度定位

UWB人员定位系统适用的场景有哪些&#xff1f;10厘米工业级实时轨迹高精度定位 一、应用场景 1、商场与零售领域&#xff1a;商场可以使用UWB人员定位系统来跟踪顾客的行踪&#xff0c;以收集顾客行为数据&#xff0c;为营销策略提供有力支持。帮助商场优化商品布局和陈列&…

在龙梦迷你电脑福珑2.0上使用Fedora 28 龙梦版

在龙梦迷你电脑福珑2.0上使用Fedora 28 龙梦版。这个版本的操作系统ISO文件是&#xff1a;Fedora28_for_loongson_MATE_Live_7.2.iso 。它在功能方面不错。能放音乐&#xff0c;能看cctv直播&#xff0c;有声音&#xff0c;能录屏&#xff0c;能在局域网里用PuTTY的ssh方式连接…

【Java EE】依赖注入DI详解

文章目录 &#x1f334;什么是依赖注入&#x1f340;依赖注入的三种方法&#x1f338;属性注入(Field Injection)&#x1f338;构造方法注入&#x1f338;Setter注入&#x1f338;三种注入优缺点分析 &#x1f333;Autowired存在的问题&#x1f332;解决Autowired对应多个对象问…

dp思维 枚举

题目链接 #include<bits/stdc.h> using namespace std; #define i64 long long const i64 mod 1e9 7; int main() {int n;cin >> n;vector<char>s(n 1);for (int i 1; i < n; i) {cin >> s[i];}//用ans记录所有满足条件的答案数量&#xff0c;c…

SQL增加主键约束的条件

结论 常见认为设为主键的条件为&#xff1a; 值唯一不含空值 具体实施中会出现各种问题 添加主键约束的条件细则&#xff1a; 值唯一数据中不含空值在定义时需要not null约束&#xff08;使用check约束不行&#xff09; 验证实验 接下来我做了关于这个细则的验证实验&am…
最新文章