【刷题之路】LeetCode 234. 回文链表

news2024/9/23 11:20:35

【刷题之路】LeetCode 234. 回文链表

  • 一、题目描述
  • 二、解题
    • 1、方法1——复制值到数组后用双指针
      • 1.1、思路分析
      • 1.2、代码实现
    • 2、方法2——反转另一半链表
      • 2.1、思路分析
      • 2.2、代码实现
      • 2.3、补充
    • 3、方法3——递归
      • 3.1、思路分析
      • 3.2、代码实现

一、题目描述

原题连接: 234. 回文链表
题目描述:
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:

在这里插入图片描述
输入: head = [1,2,2,1]
输出: true

示例 2:

在这里插入图片描述
输入: head = [1,2]
输出: false

提示:
链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

二、解题

1、方法1——复制值到数组后用双指针

1.1、思路分析

一个最容易想到的方法就是先将链表个节点的值赋值到一个等长的数组中,然后再在数组上使用双指针来判断数组是否回文:
在这里插入图片描述

1.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

bool isPalindrome(struct ListNode* head) {
    struct ListNode* cur = head;

    // 先算出链表的长度
    int len = 0;
    while (cur) {
        len++;
        cur = cur->next;
    }

    // 创建数组
    int* temp = (int*)malloc(len * sizeof(int));
    if (NULL == temp) {
        perror("malloc fail");
        exit(-1);
    }

    // 将链表的值复制到数组
    cur = head;
    int i = 0;
    while (cur) {
        temp[i] = cur->val;
        cur = cur->next;
        i++;
    }

    // 使用双指针判断
    int left = 0;
    int right = len - 1;
    while (left < right) {
        if (temp[left] != temp[right]) {
            free(temp);
            temp = NULL;
            return false;
        }
        left++;
        right--;
    }
    free(temp);
    temp = NULL;
    return true;
}

时间复杂度:O(n),n为链表的长度。
空间复杂度:O(n),我们需要额外的n个空间来存储链表节点的值,故空间复杂度为O(n)。

2、方法2——反转另一半链表

2.1、思路分析

因为是单链表,所以我们不能从后往前遍历,但我们可以把链表反转之后再从前往后遍历,这也等同于从后万千遍历。
所以我们可以先将后半部分的链表反转过来之后,再将它与前半部分的链表同步遍历进行比较:
在这里插入图片描述
而为了反转后一半链表我们得先找到链表的中间节点,我们可以沿用[LeetCode 876. 链表的中间结点]中经典的快慢指针方法。
而反转操作我们就可以沿用[LeetCode 206. 反转链表]的头插法。
最后我们使用两个指针同步遍历两个链表:
在这里插入图片描述
只要遇到一个cur1->val != cur2->val的情况就可以返回false,当其中一个指针为空时就可以停止,并返回true。

2.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

// 先写一个函数,返回链表的中间节点
struct ListNode *FindMid(struct ListNode *head) {
    if (NULL == head) {
        return NULL;
    }
    struct ListNode *fast = head; // 快指针,一次走两步
    struct ListNode *slow = head; // 慢指针,一次走一步
    while (fast && fast->next) {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

// 再写一个函数,将一个链表翻转
struct ListNode *ReverseList(struct ListNode *head) {
    if (NULL == head) {
        return NULL;
    }
    struct ListNode *newhead = NULL;
    struct ListNode *cur = head;
    struct ListNode *pre = NULL; // cur的前一个节点
    while (cur) {
        pre = cur;
        cur = cur->next;

        // 头插
        pre->next = newhead;
        newhead = pre;
    }
    return newhead;
}
bool isPalindrome(struct ListNode* head){
    struct ListNode *head1 = head;
    struct ListNode *mid = FindMid(head);
    struct ListNode *head2 = ReverseList(mid);
    struct ListNode *cur1 = head1;
    struct ListNode *cur2 = head2;
    while (cur1 && cur2) {
        if (cur1->val != cur2->val) {
            return false;
        }
        cur1 = cur1->next;
        cur2 = cur2->next;
    }
    return true;
}

时间复杂度:O(n),n为链表的长度。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

2.3、补充

可能有人看到代码后会异或,我的代码里并没有向上面图解中的那样把前半部分的链表的尾的next个置空啊:
在这里插入图片描述
在这里插入图片描述
其实这一步是可有可无的,而我们必须要做的是将后半部分反转后的链表的尾节点的next置空。因为如果原链表的节点数是偶数,后半部分的链表的节点是也是偶数,也就是后半部分的节点数是原链表节点数的一半,所以即使我们没有吧前半部分的尾给置空,那时也是cur2先为空:
在这里插入图片描述
而当原链表的节点数为奇数时,后半部分的链表就会比前半部分多出一个节点:
在这里插入图片描述
但我们发现cur1和cur2会同时到达反转部分的最后一个节点,最后也同时为空:
在这里插入图片描述
所以说,将前半部分的尾置空的操作是可有可无的。

3、方法3——递归

3.1、思路分析

递归的思路其实类似于双指针,先让递归一层层深入,直到走到链表的尾,在通过一层层的返回来模拟指针从后往前走的动作。
我们需要额外创建一个全局的指针,在每次递归中,如果本次递归满足前面的节点的val和后面的节点的val相同,就让前面的节点往后走一步。如果不相同就直接返回false。

3.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

// 定义一个全局的节点指针
 struct ListNode *leftNode;
 // 先写一个递归函数
 bool recursivelyCheck(struct ListNode *rightNode) {
     if (rightNode) {
         if (!recursivelyCheck(rightNode->next)) {
             return false;
         }
         if (leftNode->val != rightNode->val) {
             return false;
         }
        leftNode = leftNode->next;
     }
     return true;
 }

bool isPalindrome(struct ListNode* head) {
    leftNode = head;
    return recursivelyCheck(head);

}

时间复杂度:O(n),n为链表的长度。
空间复杂度:O(n),空间复杂度主要取决于递归的最大深度,这里的最大深度就是链表的长度,故空间复杂度为O(n)。

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

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

相关文章

计算机图形学 | 裁剪与屏幕映射

计算机图形学 | 裁剪与屏幕映射 计算机图形学 | 裁剪与屏幕映射8.1 裁剪思想裁剪的概念编码裁剪法中点裁剪法Liang-Barsky算法 8.2 真正的裁剪——在三维空间遇见多边形真正的裁剪多边形的裁剪Weiler-Atherton算法三维空间中的裁剪 8.3 几何阶段的完结&#xff1a;屏幕映射屏幕…

API 接口的使用和功能

随着互联网的快速发展&#xff0c;API接口已经成为了现代开发中不可或缺的一部分。API接口可以让你的应用程序与其他应用程序、系统或服务进行数据交流和集成。如果你正在开发应用程序&#xff0c;那么最好的方法就是使用API接口来增强功能和性能。 我们的API接口是为您的应用…

上财黄烨:金融科技人才的吸引与培养

“金融科技企业在吸引人才前&#xff0c;应先完善人才培养机制&#xff0c;建立员工画像&#xff0c;有针对性地培训提高成员综合素质。” ——上海金融智能工程技术研究中心上海财经大学金融科技研究院秘书长&院长助理黄烨老师 01.何为数字人才&#xff1f; 目前大多数研…

什么,你不会Windows本地账户和本地组账户的管理加固?没意思

什么&#xff0c;你不会Windows本地账户和本地组账户的管理加固&#xff1f;没意思 1.图形化界面方式管理用户2.图形化界面方式管理用户组3.命令行界面方式管理用户4.命令行界面方式管理账户组5.账户安全基线加固账户检查口令检查 1.图形化界面方式管理用户 1、打开管理界面 …

运维自动化工具 Ansible的安装部署和常用模块介绍

ansible安装 ansible的安装有很多种方式 官方文档&#xff1a;https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.ht ml https://docs.ansible.com/ansible/latest/installation_guide/index.html 下载 https://releases.ansible.com/ansible…

Java入门全网最详细 - 从入门到转行

Java基础入门 - 坚持 Java 基本介绍Java 学习须知Java 学习文档Java 基础Java Hello WorldJava 变量Java 数据类型Java 运算符Java 修饰符Java 表达式 & 语句 & 代码块Java 注释--------------------------------------------------------------------------Java 控制语…

在vue中引入高德地图

既然要用到高德地图首先要申请成为高德地图开发者&#xff0c;并申请使用高德地图的key这两点在这篇文章就不过多赘述&#xff0c;有需要的小伙伴可以查查资料&#xff0c;或者去高德地图api官网都有很详细的介绍。高德地图官网 简单提一下申请秘钥流程&#xff08;web端&#…

Python入门教程+项目实战-12.2节: 字典的操作方法

目录 12.2.1 字典的常用操作方法 12.2.2 字典的查找 12.2.3 字典的修改 12.2.4 字典的添加 12.2.5 字典的删除 12.2.6 知识要点 12.2.7 系统学习python 12.2.1 字典的常用操作方法 字典类型是一种抽象数据类型&#xff0c;抽象数据类型定义了数据类型的操作方法&#x…

想成为神经网络大师?这些常用算法和框架必须掌握!

神经网络是机器学习和人工智能领域中的一种常用算法&#xff0c;它在图像识别、自然语言处理等方面都有广泛的应用。如果你想入门神经网络&#xff0c;那么这篇文章就是为你准备的。 首先&#xff0c;了解基本概念是入门神经网络的基础。神经元是神经网络的基本组成部分&#x…

AQS底层源码解析

可重入锁 又叫递归锁&#xff0c;同一个线程在外层方法获得锁的时候&#xff0c;再进入该线程内层方法会自动获取锁&#xff0c;&#xff08;前提锁对象是同一个对象&#xff09;。不会因为之前已经获取过还没释放而阻塞。 Synchronized和ReentrantLock都是可重入锁&#xff…

玩游戏时突然弹出”显示器驱动程序已停止响应并且已恢复”怎么办

随着3A游戏大作不断面市&#xff0c;用户也不断地提升着自己的硬件设备。但是硬件更上了&#xff0c;却还会出现一些突如其来的情况&#xff0c;比如正准备开启某款游戏时&#xff0c;电脑右下角突然出现“显示器驱动程序已停止响应并且已恢复”。遇事不慌&#xff0c;驱动人生…

创新指南|5大策略让创新业务扩张最大避免“增长痛苦”

公司在开发和孵化新业务计划方面进行了大量投资&#xff0c;但很少有公司遵循严格的途径来扩大新业务规模。虽然80%的公司声称构思和孵化新企业&#xff0c;但只有16%的公司成功扩大了规模。典型案例是百思买在许多失败倒闭的扩大新业务取得了成功。它经历了建立新业务所需的3个…

如何使用 Python+selenium 进行 web 自动化测试?

使用Pythonselenium进行web自动化测试主要分为以下步骤&#xff1a; 在华为工作了10年的大佬出的Web自动化测试教程&#xff0c;华为现用技术教程&#xff01;_哔哩哔哩_bilibili在华为工作了10年的大佬出的Web自动化测试教程&#xff0c;华为现用技术教程&#xff01;共计16条…

VMware ESXi 7.0 U3m macOS Unlocker OEM BIOS (标准版和厂商定制版)

VMware ESXi 7.0 U3m macOS Unlocker & OEM BIOS (标准版和厂商定制版) 提供标准版和 Dell (戴尔)、HPE (慧与)、Lenovo (联想)、Inspur (浪潮)、Cisco (思科) 定制版镜像 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-7-u3-oem/&#xff0c;查看最新版…

AC/DC、DC/DC转换器

什么是AC&#xff1f; Alternating Current&#xff08;交流&#xff09;的首字母缩写。 AC是大小和极性&#xff08;方向&#xff09;随时间呈周期性变化的电流。 电流极性在1秒内的变化次数被称为频率&#xff0c;以Hz为单位表示。 什么是DC&#xff1f; Direct Current&…

C语言的存储类别,链接和内存管理

目录 1.1作用域 1.2链接 1.3存储期 1.4存储类别 1.4.1自动变量 1.4.2寄存器变量 1.4.3块作用域的静态变量 1.4.4外部链接的静态变量 1.4.5内部链接的静态变量 1.4.6存储类别说明符 1.5动态内存管理 1.5.1出现原因 栈内存 数据段与代码段 堆内存 1.5.2动态内存函…

Flink第二章:基本操作

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作 文章目录 系列文章目录前言一、Source1.读取无界数据流2.读取无界流数据3.从Kafka读取数据 二、Transform1.map(映射)2.filter(过滤)3.flatmap(扁平映射)4.keyBy(按键聚合)5.reduce(归约聚合)6.UDF(用户自定义函数)7.…

4个ChatGPT拓展出来的工具

现在ChatGPT 相关 的方向非常的多&#xff0c;各个大厂一个一个推出了自己的大模型&#xff0c;从国外到国内&#xff0c;ChatGPT 相关 也有几十个&#xff0c;这是大厂的方向。 对于比较小的团队&#xff0c;很多都是在ChatGPT 的基础上进行的开发&#xff0c;下面罗列出4个在…

ASO优化之应用内活动的投放策略

我们可以在“落地页”&#xff0c;“搜索结果页”&#xff0c;“详情页”&#xff0c;“today标签页”等各个版面展示应用的活动投放&#xff0c;这不仅能够快速被用户浏览到&#xff0c;自然能带来更多的流量&#xff0c;还能促进用户的活跃度。 那我们该如何进行投放呢&…

哪一本书让你逢人就推荐的?

小编逢人就推荐的程序员经典书目&#xff1a; 1、【樊登推荐】浪潮之巅 第四版 作者&#xff1a;吴军 这不是一本科技产业发展历史集&#xff0c;而是在这个数字时代&#xff0c;一本IT人非读不可&#xff0c;而非IT人也应该拜读的作品。 《浪潮之巅 第四版》是一本介绍互联…