【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;
    }
};

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

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

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

相关文章

OLED透明屏在零售行业有哪些优势

OLED透明屏在零售行业具有诸多优势&#xff0c;这些优势使得它成为零售行业中一种创新且高效的展示工具。以下是对OLED透明屏在零售行业优势的详细分析&#xff1a; 1. 视觉吸引力与沉浸感 高透明度&#xff1a;OLED透明屏能够实现40%以上的透明度&#xff0c;使得屏幕后的物体…

kali搭建pikachu靶场

前言&#xff1a; 总所周知搭个网站需要有apachemysqlphp&#xff0c;Apache是一个开源的Web服务器软件&#xff0c; MySQL是一种关系型数据库管理系统&#xff08;数据库&#xff09;&#xff0c;PHP是一种在服务器上执行的脚本语言 文章内容来自&#xff1a;【黑帽编程与攻…

学习threejs,对模型多个动画切换展示

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.AnimationMixer 动画…

【再谈设计模式】适配器模式 ~接口兼容的桥梁

一、引言 在软件开发的复杂世界里&#xff0c;不同的组件、类或者系统往往有着各自独立的设计和接口定义。当需要将这些原本不兼容的部分整合在一起协同工作时&#xff0c;就像尝试将方形的榫头插入圆形的卯眼一样困难。适配器设计模式就如同一位神奇的工匠&#xff0c;能够巧妙…

光猫、路由器、交换机之连接使用(Connection and Usage of Optical Cats, Routers, and Switches)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

《深入理解 Spring MVC 工作流程》

一、Spring MVC 架构概述 Spring MVC 是一个基于 Java 的轻量级 Web 应用框架&#xff0c;它遵循了经典的 MVC&#xff08;Model-View-Controller&#xff09;设计模式&#xff0c;将请求、响应和业务逻辑分离&#xff0c;从而构建出灵活可维护的 Web 应用程序。 在 Spring MV…

【Python小技巧】高效实现文件批量重命名

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

ES分词环境实战

文章目录 安装下载1.1 下载镜像1.2 单节点启动 防火墙设置异常处理【1】iptable链路中断 参考文档 参加完2024年11月软考&#xff0c;对ES的分词进行考查&#xff0c;前期有【 Docker 环境下安装部署 Elasticsearch 和 kibana】和【 Docker 环境下为 Elasticsearch 安装IK 分…

华为云stack网络服务流量走向

1.同VPC同子网同主机内ECS间互访流量走向 一句话通过主机内部br-int通信 2.同VPC同子网跨主机ECS间互访流量走向 3.同VPC不同子网同主机ECS间互访流量走向 去往本机的mac地址都记录在br-tun流表里 4.同VPC不同子网跨主机ECS间互访流量走向 5.对等连接流量走向&#xff08;跨V…

计算机网络:运输层 —— TCP 的拥塞控制

文章目录 TCP的拥塞控制拥塞控制的基本方法流量控制与拥塞控制的区别拥塞控制分类闭环拥塞控制算法 TCP的四种拥塞控制方法&#xff08;算法&#xff09;窗口慢开始门限慢开始算法拥塞避免算法快重传算法快恢复算法 TCP拥塞控制的流程TCP拥塞控制与网际层拥塞控制的关系 TCP的拥…

利用uniapp开发鸿蒙:运行到鸿蒙模拟器—踩坑合集

从uniapp运行到鸿蒙模拟器上这一步&#xff0c;就有非常多的坑&#xff0c;一些常见的坑&#xff0c;官网都有介绍&#xff0c;就不再拿出来了&#xff0c;这里记录一下官网未记录的大坑 1.运行路径从hbuilderx启动鸿蒙模拟器 解决方法&#xff1a; Windows系统&#xff0c;官…

linux 常用命令指南(存储分区、存储挂载、docker迁移)

前言&#xff1a;由于目前机器存储空间不够&#xff0c;所以‘斥巨资’加了一块2T的机械硬盘&#xff0c;下面是对linux扩容的一系列操作&#xff0c;包含了磁盘空间的创建、删除&#xff1b;存储挂载&#xff1b;docker迁移&#xff1b;anaconda3迁移等。 一、存储分区 1.1 …

layui合并table相同内的行

<table border"1" id"table1" class"layui-table"><thead><tr><th><b>姓名</b></th><th><b>项目</b></th><th><b>任务</b></th><th><b>…

C++刷题强训(day10)--最长回文子串、买股票的最好时期(一)、过河卒

目录 1、最长回文子串 1.1 题目 1.2 思路 1.3 代码实现 2、买卖股票的最好时机 2.1 题目 2.2 思路 2.3 代码实现 3、过河卒 3.1 题目 3.2 思路 3.3 代码实现 1、最长回文子串 1.1 题目 1.2 思路 根据题目可知&#xff0c;在一个长度为n的字符串中求得最长回文子…

【蓝桥杯C/C++】翻转游戏:多种实现与解法解析

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: 蓝桥杯C/C 文章目录 &#x1f4af;题目&#x1f4af;问题分析解法一&#xff1a;减法法解法二&#xff1a;位运算解法解法三&#xff1a;逻辑非解法解法四&#xff1a;条件运算符解法解法五&#xff1a;数组映射法不同解法的比较…

Debezium-BinaryLogClient

文章目录 概要核心流程技术名词解释技术细节小结 概要 BinaryLogClient类&#xff0c;用于连接和监听 MySQL 服务器的二进制日志&#xff08;binlog&#xff09; 核心流程 技术名词解释 ### GTID (Global Transaction Identifier) 理解 #### 定义 GTID&#xff08;Global Tra…

嵌入式linux中QT信号与槽基本操作与实现

大家好,今天主要给大家分享一下,如何使用linux系统上的QT进行界面开发与实现。 第一:QT的信号与槽基本简介 在操作QT的时候,可以使用里面的信号与槽。所谓信号就是一个对象发出的信号,槽就是当这个对象发出这个信号时,对应连接的槽就发被执行或者触发。 进行信号与槽的连…

03 —— Webpack 自动生成 html 文件

HtmlWebpackPlugin | webpack 中文文档 | webpack中文文档 | webpack中文网 安装 npm install --save-dev html-webpack-plugin 下载html-webpack-plugin本地软件包 npm i html-webpack-plugin --save-dev 配置webpack.config.js让webpack拥有插件功能 const HtmlWebpack…

大模型时代的具身智能系列专题(十二)

Robert Platt(波士顿动力) Robert Platt是美国东北大学Helping Hands机器人实验室主任、计算机科学教授。在加入东北大学之前&#xff0c;Platt 曾是麻省理工学院的研究科学家和美国宇航局的机器人工程师。platt博士毕业于马萨诸塞大学阿默斯特分校计算机科学专业。Platt 的工…

【软件测试】设计测试用例的万能公式

文章目录 概念设计测试用例的万能公式常规思考逆向思维发散性思维万能公式水杯测试弱网测试如何进行弱网测试 安装卸载测试 概念 什么是测试用例&#xff1f; 测试⽤例&#xff08;Test Case&#xff09;是为了实施测试⽽向被测试的系统提供的⼀组集合&#xff0c;这组集合包…