algorithm-base/animation-simulation/链表篇/234. 回文链表.md

132 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

> 如果阅读时,发现错误,或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
>
> 感谢支持,该仓库会一直维护,希望对各位有一丢丢帮助。
>
> 另外希望手机阅读的同学可以来我的 <u>[**公众号:袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
#### [234. 回文链表](https://leetcode-cn.com/problems/palindrome-linked-list/)
请判断一个链表是否为回文链表。
示例 1:
```
输入: 1->2
输出: false
```
示例 2:
```java
输入: 1->2->2->1
输出: true
```
题目解析:
题目理解起来很简单判断是否为回文如果单纯判断一个字符串或者数组是不是回文很容易。因为数组查询元素的时间复杂度为O(1)但是链表的查询时间复杂度为O(n),而且题目中的链表为单链表,指针只能后移不能前移。所以我们判断起来会比较困难。
巧用数组法:
我们首先将链表的所有元素都保存在数组中,然后再利用双指针遍历数组,进而来判断是否为回文。这个方法很容易理解,而且代码实现也比较简单。
```java
class Solution {
public boolean isPalindrome(ListNode head) {
//这里需要用动态数组,因为我们不知道链表的长度
List<Integer> arr = new ArrayList<Integer>();
ListNode copynode = head;
//将链表的值复制到数组中
while (copynode != null) {
arr.add(copynode.val);
copynode = copynode.next;
}
// 双指针遍历数组
int back = 0;
int pro = arr.size() - 1;
while (back < pro) {
//判断两个指针的值是否相等
if (!arr.get(pro).equals(arr.get(back))) {
return false;
}
//移动指针
back++;
pro--;
}
return true;
}
}
```
这个方法可以直接通过,但是这个方法需要辅助数组,那我们还有其他更好的方法吗?
双指针翻转链表法
在上个题目中我们知道了如何找到链表的中间节点,那我们可以在找到中间节点之后,对后半部分进行翻转,翻转之后,重新遍历前半部分和后半部分进行判断是否为回文。
动图解析:
![翻转链表部分](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/翻转链表部分.1v2ncl72ligw.gif)
```java
class Solution {
public boolean isPalindrome(ListNode head) {
if (head==null || head.next==null) {
return true;
}
//找到中间节点,也就是翻转的头节点,这个在昨天的题目中讲到
//但是今天和昨天有一些不一样的地方就是,如果有两个中间节点返回第一个,昨天的题目是第二个
ListNode midenode = searchmidnode(head);
//原地翻转链表,需要两个辅助指针。这个也是面试题目,大家可以做一下
//这里我们用的是midnode.next需要注意因为我们找到的是中点但是我们翻转的是后半部分
ListNode backhalf = reverse(midenode.next);
//遍历两部分链表,判断值是否相等
ListNode p1 = head;
ListNode p2 = backhalf;
while (p2 != null) {
if (p1.val != p2.val) {
return false;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表并返回结果,这一步是需要注意的,我们不可以破坏初始结构,我们只是判断是否为回文,
//当然如果没有这一步也是可以AC但是面试的时候题目要求可能会有这一条。
midenode.next = reverse(backhalf);
return true;
}
//找到中间的部分
public ListNode searchmidnode (ListNode head) {
ListNode fast = new ListNode(-1);
ListNode slow = new ListNode(-1);
fast = head;
slow = head;
//找到中点
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
//翻转链表
public ListNode reverse (ListNode slow) {
ListNode low = null;
ListNode temp = null;
//翻转链表
while (slow != null) {
temp = slow.next;
slow.next = low;
low = slow;
slow = temp;
}
return low;
}
}
```