【HOT100第五天】搜索二维矩阵 II,相交链表,反转链表,回文链表

240.搜索二维矩阵 II

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

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

先动手写写最简单方法,二重循环。

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m=matrix.size(),n=matrix[0].size();if(m==0||n==0) return{};for(auto& itx:matrix){for(auto& itxy:itx){if(itxy==target) return true;}}return false;}
};

优化方法的话,根据昨天做的题,感觉大多数矩阵相关的题目都是依靠找规律,那我们再看看例图:

 以“5”这一项为例,左边和上边的数比 5 小,右边和下边的数比 5 大。可以利用这一特点,从左下角或者右上角开始顺藤摸瓜。

以从右上角为例,如果target小于15,就向左移一位,如果target大于15,就向右移一位,以此类推,如果已经移出了矩阵的边界,说明没找到target的值,就返回false。

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m=matrix.size(),n=matrix[0].size();if(m==0||n==0) return{};int x=0,y=n-1;while(x<=m-1&&y>=0){if(matrix[x][y]==target) return true;else if(matrix[x][y]>target){y--;}else{x++;}}return false;}
};

160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

 虽然是简单题但其实是最让我痛苦的一道。!看一眼题目感觉还好理解,一看给的例子就懵了。

好了这种情况下先不要急,看一下方法体吧。输入是两个链表的头结点,输出的是相交节点。那目的先可以初步确定了。

一开始看到可能会没什么思路,这是正常的,因为题目对相交链表的表述不太清楚。这道题中相交链表的定义是一个从某一结点开始,两链表相交后,后面的结点完全相同。

验证一下它说的是不是这样,我添加了一个用例,在中间的某一位置相交,又在后面分开:

可见确实如此,糟老头子坏得很啊。

这样的话,两个链表相交的过程就可以简化成下面这样了👇

a1->a2->...->ai->c1->c2->...->cn
b1->b2->...->bj

设想一下,如果 1~i 和 1~j 的值相同就好了,那就意味着这两个链表长度相同,A和B链表上的指针到达相交结点的时间是相同的。

但事实是两个链表不一定长度相同,不过思路还是一样,是不是只要A和B两个链表的指针同时到达相交结点,就可以找到这个结点了?

我们现在已知,遍历A链表需要指针移动(i+n)次,遍历B链表需要指针移动(j+n)次,不如让A链表的指针遍历完A后再遍历一遍B链表,并且B链表指针遍历完B后再遍历A链表,这样的话,每一个指针都一共要移动(i+j+2n)次,那么第一个找到的满足(pa==pb)的结点,就一定是相交结点。

如果两个链表没有重合也不用担心,两个指针会在链表的尾部相遇,然后返回一个空指针。

这样就可以写出代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if(headA==nullptr||headB==nullptr) return nullptr;ListNode *pa=headB,*pb=headA;while(pa!=pb){if(pa) pa=pa->next;else pa=headB;if(pb) pb=pb->next;else pb=headA;}return pa;}
};

但再具体想一想,如果两链表后面的结点不一样,只有中间连续的1~n的结点相交的情况下,这个方法能不能用呢?答案是不能!因为相交结点后面的长度会影响到指针到达相交节点。比如说链表A如果是(i1+n+i2),链表B是(j1+n+j2),那用之前的两个都遍历的方法,A的指针pa第二次到达相交结点走过的路程是(i1+n+i2+j1),而链表B第二次到达相交结点走过的路程是(j1+n+j2+i1)!!!

 206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

反转无非是把 a1 -> a2 -> a3 ->..._> an -> nullptr 改成 nullptr <- a1 <- a2 <-...<- an。

比如遍历用的指针是p,我们把p的next改成前驱结点就好了。不过前驱结点需要提前记录,而且为了在改变了p->next之后还可以继续遍历,就需要先把后继p->next存下来再改成前驱即可。所以整理一下,对结点的调整步骤为:

1. 保存后继

2. 把next改成前驱结点

3. 把当前结点改成前驱

4. 让p移向下一个结点

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {if(!head) return nullptr;ListNode* p=head;ListNode* pre=nullptr;while(p!=nullptr){ListNode* next=p->next;p->next=pre;pre=p;p=next;}return pre;}
};

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

克服恐惧的最好方法就是避免恐惧(。。),不会操作链表但是向想写出来,就先把它复制进数组里再对它使用双指针!过了就是胜利✌

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:bool isPalindrome(ListNode* head) {vector<int> nums;while(head!=nullptr){nums.emplace_back(head->val);head=head->next;}int left=0,right=nums.size()-1;while(left<right){if(nums[left]==nums[right]){left++;right--;}else{return false;}}return true;}
};

说笑了,下面看看正规方法来做。

 我们刚刚既然已经写过了反转链表,那这一次不如用上。

直接在它给的链表上改,把前一半链表都反转,后一半链表不变,然后用两个指针分别遍历前后两段,一边遍历一边比较。

如何实现只反转一半?怎么找到链表的中点?这里可以使用快慢指针,快指针一次进两格,慢指针一次进一格,那么当fast或者fast->next为空时,slow所在的位置就是中点了。

不过要注意,当结点数为奇数时,我们需要用中点后面那个点作为后半部分的起始点。 

如何判断链表是奇数个还是偶数个?在纸上画一下,以题目提供的测试用例为例:

fast经历的结点索引值是0,2,“4”。所以当结点是偶数个时,fast==nullptr,以此类推,当结点为奇数个时,fast->next=nullptr。

梳理一下目前我们用到的变量,快慢指针是肯定的,fast用来确定奇偶和中点,slow摸到中点顺便用于反转链表,pre是反转用到的前驱结点,next是反转用到的后继结点。

当摸到中点并且完成反转时,我们的链表变成了这样👇(以4个和5个结点的链表为例)

a1 <- a2   a3 -> a4 -> a5 【pre在a2的位置,slow在a3的位置,next在a4的位置,fast在a5的位置】

a1 <- a2  a3 -> a4 【pre在a2的位置,slow在a3的位置,next在a4的位置,fast在a4后面的位置】

因此,偶数个结点的话用pre和slow来遍历一下链表就行了,奇数个结点的话就把slow处理一下再遍历。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:bool isPalindrome(ListNode* head) {if(head==nullptr||head->next==nullptr) return true;ListNode* slow=head;ListNode* fast=head;ListNode* pre=nullptr;ListNode* next=nullptr;//快指针在链表尾部,慢指针在中点while(fast&&fast->next){fast=fast->next->next;next=slow->next;slow->next=pre;pre=slow;slow=next;}//fast为空是偶数,fast不为空是奇数if(fast!=nullptr){slow=slow->next;}//利用pre和slow比较是否相等while(slow!=nullptr&&pre!=nullptr){if(slow->val!=pre->val){return false;}slow=slow->next;pre=pre->next;}return true;}
};

这题下次还是用复制进数组的方法吧(。。。。)

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

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

相关文章

模板元函数应用:输出字符串。

看下面三个字符串&#xff0c;s1,s2,s3 &#xff1a; string s1 "逆天邪神";wstring s2 _t("焚星妖莲");_string s3 "焚绝尘"; 在控制台输出字符串&#xff0c;可能的一个方案是&#xff1a; void print_test(const wstring& s) {std::…

pytest | 框架的简单使用

这里写目录标题 单个文件测试方法执行测试套件的子集测试名称的子字符串根据应用的标记进行选择 其他常见的测试命令 pytest框架的使用示例 pytest将运行当前目录及其子目录中test_*.py或 *_test.py 形式的所有 文件 文件内的函数名称可以test* 或者test_* 开头 单个文件测试…

【C++】类和对象-深度剖析默认成员函数-上

> &#x1f343; 本系列为初阶C的内容&#xff0c;如果感兴趣&#xff0c;欢迎订阅&#x1f6a9; > &#x1f38a;个人主页:[小编的个人主页])小编的个人主页 > &#x1f380; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 > ✌️ &#x1f91e; &#x1…

Web性能优化:从基础到高级

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Web性能优化&#xff1a;从基础到高级 Web性能优化&#xff1a;从基础到高级 Web性能优化&#xff1a;从基础到高级 引言 基础优…

当 docker-compose.yaml 文件部署时,Dify 线上版本升级过程

如果线上 Dify 是通过 docker-compose.yaml 文件部署的&#xff0c;那么当 Dify 版本升级时该如何操作呢&#xff1f;官方已经给出了 Docker compose 和 Source Code 两种方式。相对而言&#xff0c;前者更简单些&#xff0c;至少不需要安装依赖包和迁移数据库文件。为了更加具…

如何让手机ip变成动态

在数字化浪潮中&#xff0c;手机已成为我们日常生活中不可或缺的一部分。无论是浏览网页、使用社交媒体还是进行在线购物&#xff0c;手机都扮演着举足轻重的角色。然而&#xff0c;在享受网络带来的便利时&#xff0c;我们也需要关注网络安全和隐私保护。静态IP地址可能让手机…

vue3 如何调用第三方npm包内部的 pinia 状态管理库方法

抛砖引玉: 如果在开发vue3项目是, 引用了npm第三方包 ,而且这个包内使用了Pinia 状态管理库,那我们如何去调用 npm内部的 Pinia 状态管理库呢? 实际遇到的问题: 今天在制作npm包时遇到的问题,之前Vue2版本的时候状态管理库用的Vuex ,当时调用npm包内的状态管理库很简单,直接引…

Linux笔记---调试工具GDB(gdb)

1. gdb的概念 GDB&#xff0c;全称GNU Debugger&#xff0c;是一个功能强大的开源调试工具&#xff0c;广泛用于Unix和类Unix系统&#xff0c;以及Microsoft Windows和macOS平台。GDB允许开发者在程序执行过程中查看内部运行情况&#xff0c;帮助定位和修复程序中的错误。 gd…

编译器gcc/g++

gcc 只用来编译C g 编译C/C 1.预处理&#xff08;进行宏替换/去注释/条件编译/头文件展开等&#xff09; 先创建 code.c 文件 -E --> 从现在开始&#xff0c;进行程序的翻译&#xff0c;一旦预处理做完&#xff0c;就停下来 -o --> 表明 -o 后面的文件名称 code…

一.安装版本为19c的Oracle数据库管理系统(Oracle系列)

1.数据库版本信息&#xff1a; 版本信息&#xff1a; 或者直接由命令查出来&#xff1a; 2.操作系统的版本信息 3.安装包下载与上传 可以去oracle官网下载也可以从其他人的百度网盘链接中下载&#xff1a; 使用xftp工具或者其他的工具&#xff08;mobaxterm&#xff09;上传到l…

DimensionX 部署笔记

目录 生成视频用CogVideoX-5b-I2V 推理代码&#xff1a; DimensionX 生成视频用CogVideoX-5b-I2V 推理代码&#xff1a; 可以生成&#xff0c;从左向右旋转的&#xff0c;也可以生成从上往下旋转的&#xff1a; import torch from diffusers import CogVideoXImageToVideo…

uni-app移动端与PC端兼容预览PDF文件

过程遇到的问题 1、如果用的是最新的版本的pdfjs的话&#xff0c;就会报Promise.withResolvers 不是一个方法的错误&#xff0c;原因是Promise.withResolvers是ES15新特性&#xff0c;想了解可参考链接&#xff0c;这里的解决方案是将插件里的涉及到Promise.withResolvers的地…

Node.js | Yarn下载安装与环境配置

一、安装Node.js Yarn 是 Node.js 下的包管理工具&#xff0c;因此想要使用 Yarn 就必须先下载 Node.js。 推荐参考&#xff1a;Node.js | npm下载安装及环境配置教程 二、Yarn安装 打开cmd&#xff0c;输入以下命令&#xff1a; npm install -g yarn检查是否安装成功&…

开源TTS语音克隆神器GPT-SoVITS_V2版本地整合包部署与远程使用生成音频

文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 本文主要介绍如何在Windows系统电脑使用整合包一键部署开源TTS语音克隆神器GPT-SoVITS&#xff0c;并结合cpolar内网穿透工…

[Docker#11] 容器编排 | .yml | up | 实验: 部署WordPress

目录 1. 什么是 Docker Compose 生活案例 2. 为什么要使用 Docker Compose Docker Compose 的安装 Docker Compose 的功能 使用步骤 核心功能 Docker Compose 使用场景 Docker Compose 文件&#xff08;docker-compose.yml&#xff09; 模仿示例 文件基本结构及常见…

鸿蒙NEXT应用示例:切换图片动画

【引言】 在鸿蒙NEXT应用开发中&#xff0c;实现图片切换动画是一项常见的需求。本文将介绍如何使用鸿蒙应用框架中的组件和动画功能&#xff0c;实现不同类型的图片切换动画效果。 【环境准备】 电脑系统&#xff1a;windows 10 开发工具&#xff1a;DevEco Studio NEXT B…

【spring 】Spring Cloud Gateway 的Filter学习

介绍和使用场景 Spring Cloud Gateway 是一个基于 Spring Framework 5 和 Project Reactor 的 API 网关&#xff0c;它旨在为微服务架构提供一种简单而有效的方式来处理请求路由、过滤、限流等功能。在 Spring Cloud Gateway 中&#xff0c;Filter 扮演着非常重要的角色&#…

opencv(c++)图像的灰度转换

opencv(c)图像的灰度转换 quickopencv.h #pragma once #include <opencv2/opencv.hpp> using namespace cv; class QuickDemo { public:void colorSpace_Demo(Mat& image); };quickopencv.cpp #include "quickopencv.h"// QuickDemo类中的颜色空间演示函…

problem forward和solution backward有什么区别

Note: 在具体研究中&#xff0c;problem forward是先提出问题&#xff0c;然后围绕着科学问题设计出完美的解决方案&#xff1b;而solution backward是先盘算自己有哪些技术&#xff0c;有哪些解决方案&#xff0c;反过来去寻找和定义问题。

C++构造函数详解

构造函数详解&#xff1a;C 中对象初始化与构造函数的使用 在 C 中&#xff0c;构造函数是一种特殊的成员函数&#xff0c;它在创建对象时自动调用&#xff0c;用来初始化对象的状态。构造函数帮助我们确保每个对象在被创建时就处于一个有效的状态&#xff0c;并且在不传递任何…