leetcode每日一题
7月
1833. 雪糕的最大数量
5801. 消灭怪物的最大数量
1418. 点菜展示表
1711. 大餐计数
5792. 统计平方和三元组的数目
274. H 指数
面试题 10.02. 变位词组
6月
1744. 你能在你最喜欢的那天吃到你最喜欢的糖果吗?
42. 接雨水
206. 反转链表
234. 回文链表
141. 环形链表
474. 一和零
5777. 使数组元素相等的减少操作次数
5776. 判断矩阵经轮转后是否一致
1897. 重新分配字符使所有字符串都相等
752. 打开转盘锁
5781. 删除一个字符串中所有出现的给定子字符串
5789. 你完成的完整对局数
5788. 字符串中的最大奇数
852. 山脉数组的峰顶索引
1894. 找到需要补充粉笔的学生编号
5786. 可移除字符的最大数目
1893. 检查是否区域内所有整数都被覆盖
1899. 合并若干三元组以形成目标三元组
5799. 最美子字符串的数目
5780. 删除一个元素使数组严格递增
5月
342. 4的幂
231. 2 的幂
560. 和为K的子数组
141. 环形链表
1074. 元素和为目标值的子矩阵数量
461. 汉明距离
1190. 反转每对括号间的子串
664. 奇怪的打印机
1787. 使所有区间的异或结果为零(放弃治疗)
1707. 与数组中元素的最大异或值
810. 黑板异或游戏
1035. 不相交的线
692. 前K个高频单词
1738. 找出第 K 大的异或坐标值
993. 二叉树的堂兄弟节点
13. 罗马数字转整数
1442. 形成两个异或相等数组的三元组数目
12. 整数转罗马数字
1269. 停在原地的方案数
1734. 解码异或后的排列
1310. 子数组异或查询
872. 叶子相似的树
vditor使用说明
645. 错误的集合
930. 和相同的二元子数组
5793. 迷宫中离入口最近的出口
5843. 作为子字符串出现在单词中的字符串数目
5832. 构造元素不等于两相邻元素平均值的数组
5851. 找出不同的二进制字符串
5850. 找出数组的最大公约数
5835. 最大方阵和
n个人匹配,两两匹配,匹配多轮
本文档使用 MrDoc 发布
-
+
首页
234. 回文链表
# 题目 ## 234. 回文链表 [https://leetcode-cn.com/problems/palindrome-linked-list/](https://leetcode-cn.com/problems/palindrome-linked-list/) 请判断一个链表是否为回文链表。 **示例 1:** ``` 输入: 1->2 输出: false ``` **示例 2:** ``` 输入: 1->2->2->1 输出: true ``` **进阶:** 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? # 题解 ## 思路 方法一用数组存储但需要消耗空间,方法二用快慢指针,翻转慢指针进行对比 。 ## 代码 ```javascript /** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ /** * 暴力法,转成数组再比较 * @param {ListNode} head * @return {boolean} */ // var isPalindrome = function(head) { // const arr = []; // while(head) { // arr.push(head.val); // head = head.next; // } // let left = 0; // let right = arr.length - 1; // while (left <= right) { // if(arr[left] !== arr[right]) return false; // left++; // right--; // } // return true; // }; var isPalindrome = function(head) { if(!head || !head.next) return true; // 慢指针 let slow = head; // 快指针 let fast = head; // 快指针走完时,慢指针正好处于中间位置 while(fast && fast.next && fast.next.next){ slow = slow.next; fast = fast.next.next; } // 反转链表,从慢指针开始的位置 let revSlow = reverseList(slow); // 比较两个链表 while(revSlow && head){ if(revSlow.val !== head.val) return false; revSlow = revSlow.next; head = head.next; } return true; }; // 反转链表 function reverseList(head) { // 初始化指针 let pre = null; // 临时变量 let next = null; while(head){ // 保存下一个节点 next = head.next; // 当前节点指向pre head.next = pre; // 修改pre pre = head; // 执行下一个节点 head = next; } return pre; }; ``` ## 官方 #### 快慢指针 **思路** 避免使用 O(n)额外空间的方法就是改变输入。 我们可以将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。比较完成后我们应该将链表恢复原样。虽然不需要恢复也能通过测试用例,但是使用该函数的人通常不希望链表结构被更改。 该方法虽然可以将空间复杂度降到 O(1),但是在并发环境下,该方法也有缺点。在并发环境下,函数运行时需要锁定其他线程或进程对链表的访问,因为在函数执行过程中链表会被修改。 **算法** 整个流程可以分为以下五个步骤: 1. 找到前半部分链表的尾节点。 2. 反转后半部分链表。 3. 判断是否回文。 4. 恢复链表。 5. 返回结果。 执行步骤一,我们可以计算链表节点的数量,然后遍历链表找到前半部分的尾节点。 我们也可以使用**快慢指针**在一次遍历中找到:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。 若链表有奇数个节点,则中间的节点应该看作是前半部分。 步骤二可以使用「[206. 反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)」问题中的解决方法来反转链表的后半部分。 步骤三比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点。 步骤四与步骤二使用的函数相同,再反转一次恢复链表本身。 ```javascript const reverseList = (head) => { let prev = null; let curr = head; while (curr !== null) { let nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; } const endOfFirstHalf = (head) => { let fast = head; let slow = head; while (fast.next !== null && fast.next.next !== null) { fast = fast.next.next; slow = slow.next; } return slow; } var isPalindrome = function(head) { if (head == null) return true; // 找到前半部分链表的尾节点并反转后半部分链表 const firstHalfEnd = endOfFirstHalf(head); const secondHalfStart = reverseList(firstHalfEnd.next); // 判断是否回文 let p1 = head; let p2 = secondHalfStart; let result = true; while (result && p2 != null) { if (p1.val != p2.val) result = false; p1 = p1.next; p2 = p2.next; } // 还原链表并返回结果 firstHalfEnd.next = reverseList(secondHalfStart); return result; }; ```
czbiao
2021年6月1日 21:09
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
阅读量
次
本站总访问量
次
本站访客数
人次
Markdown文件
分享
链接
类型
密码
更新密码