2021-03-19 07:07:33 +00:00
|
|
|
|
|
|
|
|
|
|
2021-03-20 08:57:12 +00:00
|
|
|
|
> 如果阅读时,发现错误,或者动画不可以显示的问题可以添加我微信好友 **[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/)
|
2021-03-19 07:07:33 +00:00
|
|
|
|
|
|
|
|
|
请判断一个链表是否为回文链表。
|
|
|
|
|
|
|
|
|
|
示例 1:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
输入: 1->2
|
|
|
|
|
输出: false
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
示例 2:
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
输入: 1->2->2->1
|
|
|
|
|
输出: true
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
题目解析:
|
|
|
|
|
|
|
|
|
|
题目理解起来很简单,判断是否为回文,如果单纯判断一个字符串或者数组是不是回文很容易。因为数组查询元素的时间复杂度为O(1),但是链表的查询时间复杂度为O(n),而且题目中的链表为单链表,指针只能后移不能前移。所以我们判断起来会比较困难。
|
|
|
|
|
|
|
|
|
|
巧用数组法:
|
|
|
|
|
|
|
|
|
|
我们首先将链表的所有元素都保存在数组中,然后再利用双指针遍历数组,进而来判断是否为回文。这个方法很容易理解,而且代码实现也比较简单。
|
|
|
|
|
|
2021-04-28 10:28:00 +00:00
|
|
|
|
**题目代码**
|
|
|
|
|
|
2021-07-12 12:25:03 +00:00
|
|
|
|
Java Code:
|
|
|
|
|
|
2021-03-19 07:07:33 +00:00
|
|
|
|
```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;
|
|
|
|
|
}
|
2021-07-12 12:25:03 +00:00
|
|
|
|
//双指针遍历数组
|
2021-03-19 07:07:33 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2021-07-12 12:25:03 +00:00
|
|
|
|
C++ Code:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
class Solution {
|
|
|
|
|
public:
|
|
|
|
|
bool isPalindrome(ListNode* head) {
|
|
|
|
|
vector<int> arr;
|
|
|
|
|
ListNode* copynode = head;
|
|
|
|
|
while (copynode) {
|
|
|
|
|
arr.push_back(copynode->val);
|
|
|
|
|
copynode = copynode->next;
|
|
|
|
|
}
|
|
|
|
|
int back = 0;
|
|
|
|
|
int pro = arr.size() - 1;
|
|
|
|
|
while (back < pro) {
|
|
|
|
|
if (arr[back] != arr[pro]) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
back++;
|
|
|
|
|
pro--;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
JS Code:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
var isPalindrome = function(head) {
|
|
|
|
|
let arr = [];
|
|
|
|
|
let copynode = head;
|
|
|
|
|
//将链表的值复制到数组中
|
|
|
|
|
while (copynode) {
|
|
|
|
|
arr.push(copynode.val)
|
|
|
|
|
copynode = copynode.next
|
|
|
|
|
}
|
|
|
|
|
//双指针遍历数组
|
|
|
|
|
let back = 0;
|
|
|
|
|
let pro = arr.length - 1;
|
|
|
|
|
while (back < pro) {
|
|
|
|
|
//判断两个指针的值是否相等
|
|
|
|
|
if (arr[back] !== arr[pro]) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
//移动指针
|
|
|
|
|
back += 1
|
|
|
|
|
pro -= 1
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Python Code:
|
|
|
|
|
|
|
|
|
|
```py
|
|
|
|
|
class Solution:
|
|
|
|
|
def isPalindrome(self, head: ListNode) -> bool:
|
|
|
|
|
arr = []
|
|
|
|
|
copynode = head
|
|
|
|
|
# 将链表的值复制到数组中
|
|
|
|
|
while copynode is not None:
|
|
|
|
|
arr.append(copynode.val)
|
|
|
|
|
copynode = copynode.next
|
|
|
|
|
# 双指针遍历数组
|
|
|
|
|
back = 0
|
|
|
|
|
pro = len(arr) - 1
|
|
|
|
|
while back < pro:
|
|
|
|
|
# 判断两个指针的值是否相等
|
|
|
|
|
if arr[back] != arr[pro]:
|
|
|
|
|
return False
|
|
|
|
|
# 移动指针
|
|
|
|
|
back += 1
|
|
|
|
|
pro -= 1
|
|
|
|
|
return True
|
|
|
|
|
```
|
|
|
|
|
|
2021-03-19 07:07:33 +00:00
|
|
|
|
这个方法可以直接通过,但是这个方法需要辅助数组,那我们还有其他更好的方法吗?
|
|
|
|
|
|
|
|
|
|
双指针翻转链表法
|
|
|
|
|
|
|
|
|
|
在上个题目中我们知道了如何找到链表的中间节点,那我们可以在找到中间节点之后,对后半部分进行翻转,翻转之后,重新遍历前半部分和后半部分进行判断是否为回文。
|
|
|
|
|
|
|
|
|
|
动图解析:
|
|
|
|
|
|
|
|
|
|
![翻转链表部分](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/翻转链表部分.1v2ncl72ligw.gif)
|
|
|
|
|
|
2021-04-28 10:28:00 +00:00
|
|
|
|
#### **题目代码**
|
|
|
|
|
|
|
|
|
|
Java Code:
|
|
|
|
|
|
2021-03-19 07:07:33 +00:00
|
|
|
|
```java
|
|
|
|
|
class Solution {
|
|
|
|
|
public boolean isPalindrome(ListNode head) {
|
|
|
|
|
if (head==null || head.next==null) {
|
2021-07-14 14:47:50 +00:00
|
|
|
|
return true;
|
2021-03-19 07:07:33 +00:00
|
|
|
|
}
|
|
|
|
|
//找到中间节点,也就是翻转的头节点,这个在昨天的题目中讲到
|
|
|
|
|
//但是今天和昨天有一些不一样的地方就是,如果有两个中间节点返回第一个,昨天的题目是第二个
|
2021-07-14 14:47:50 +00:00
|
|
|
|
ListNode midenode = searchmidnode(head);
|
2021-03-19 07:07:33 +00:00
|
|
|
|
//原地翻转链表,需要两个辅助指针。这个也是面试题目,大家可以做一下
|
|
|
|
|
//这里我们用的是midnode.next需要注意,因为我们找到的是中点,但是我们翻转的是后半部分
|
|
|
|
|
ListNode backhalf = reverse(midenode.next);
|
|
|
|
|
//遍历两部分链表,判断值是否相等
|
|
|
|
|
ListNode p1 = head;
|
|
|
|
|
ListNode p2 = backhalf;
|
|
|
|
|
while (p2 != null) {
|
|
|
|
|
if (p1.val != p2.val) {
|
2021-07-14 14:47:50 +00:00
|
|
|
|
//若要还原,记得这里也要reverse
|
|
|
|
|
midenode.next = reverse(backhalf);
|
|
|
|
|
return false;
|
2021-03-19 07:07:33 +00:00
|
|
|
|
}
|
|
|
|
|
p1 = p1.next;
|
|
|
|
|
p2 = p2.next;
|
|
|
|
|
}
|
2021-07-14 14:47:50 +00:00
|
|
|
|
//还原链表并返回结果,这一步是需要注意的,我们不可以破坏初始结构,我们只是判断是否为回文,
|
2021-03-19 07:07:33 +00:00
|
|
|
|
//当然如果没有这一步也是可以AC,但是面试的时候题目要求可能会有这一条。
|
|
|
|
|
midenode.next = reverse(backhalf);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-07-14 14:47:50 +00:00
|
|
|
|
//找到中点
|
|
|
|
|
public ListNode searchmidnode (ListNode head) {
|
|
|
|
|
ListNode fast = head;
|
|
|
|
|
ListNode slow = head;
|
2021-03-19 07:07:33 +00:00
|
|
|
|
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) {
|
2021-07-14 14:47:50 +00:00
|
|
|
|
temp = slow.next;
|
|
|
|
|
slow.next = low;
|
|
|
|
|
low = slow;
|
|
|
|
|
slow = temp;
|
2021-03-19 07:07:33 +00:00
|
|
|
|
}
|
|
|
|
|
return low;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2021-04-28 10:28:00 +00:00
|
|
|
|
C++ Code:
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
class Solution {
|
|
|
|
|
public:
|
|
|
|
|
bool isPalindrome(ListNode* head) {
|
|
|
|
|
if (head == nullptr || head->next == nullptr) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-07-14 14:47:50 +00:00
|
|
|
|
//找到中间节点,也就是翻转的头节点,这个在昨天的题目中讲到
|
2021-04-28 10:28:00 +00:00
|
|
|
|
//但是今天和昨天有一些不一样的地方就是,如果有两个中间节点返回第一个,昨天的题目是第二个
|
2021-07-14 14:47:50 +00:00
|
|
|
|
ListNode * midenode = searchmidnode(head);
|
2021-04-28 10:28:00 +00:00
|
|
|
|
//原地翻转链表,需要两个辅助指针。这个也是面试题目,大家可以做一下
|
|
|
|
|
//这里我们用的是midnode->next需要注意,因为我们找到的是中点,但是我们翻转的是后半部分
|
|
|
|
|
ListNode * backhalf = reverse(midenode->next);
|
|
|
|
|
//遍历两部分链表,判断值是否相等
|
|
|
|
|
ListNode * p1 = head;
|
|
|
|
|
ListNode * p2 = backhalf;
|
|
|
|
|
while (p2 != nullptr) {
|
|
|
|
|
if (p1->val != p2->val) {
|
2021-07-14 14:47:50 +00:00
|
|
|
|
//若要还原,记得这里也要reverse
|
|
|
|
|
midenode.next = reverse(backhalf);
|
|
|
|
|
return false;
|
2021-04-28 10:28:00 +00:00
|
|
|
|
}
|
|
|
|
|
p1 = p1->next;
|
|
|
|
|
p2 = p2->next;
|
|
|
|
|
}
|
2021-07-14 14:47:50 +00:00
|
|
|
|
//还原链表并返回结果,这一步是需要注意的,我们不可以破坏初始结构,我们只是判断是否为回文,
|
2021-04-28 10:28:00 +00:00
|
|
|
|
//当然如果没有这一步也是可以AC,但是面试的时候题目要求可能会有这一条。
|
|
|
|
|
midenode->next = reverse(backhalf);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//找到中间的部分
|
2021-07-14 14:47:50 +00:00
|
|
|
|
ListNode * searchmidnode (ListNode * head) {
|
|
|
|
|
ListNode * fast = head;
|
|
|
|
|
ListNode * slow = head;
|
2021-04-28 10:28:00 +00:00
|
|
|
|
while (fast->next != nullptr && fast->next->next != nullptr) {
|
|
|
|
|
fast = fast->next->next;
|
|
|
|
|
slow = slow->next;
|
|
|
|
|
}
|
|
|
|
|
return slow;
|
|
|
|
|
}
|
|
|
|
|
//翻转链表
|
|
|
|
|
ListNode * reverse (ListNode * slow) {
|
|
|
|
|
ListNode * low = nullptr;
|
|
|
|
|
ListNode * temp = nullptr;
|
|
|
|
|
while (slow != nullptr) {
|
2021-07-14 14:47:50 +00:00
|
|
|
|
temp = slow->next;
|
|
|
|
|
slow->next = low;
|
|
|
|
|
low = slow;
|
|
|
|
|
slow = temp;
|
2021-04-28 10:28:00 +00:00
|
|
|
|
}
|
|
|
|
|
return low;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
2021-07-14 14:47:50 +00:00
|
|
|
|
JS Code:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
var isPalindrome = function(head) {
|
|
|
|
|
if (head === null || head.next === null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//找到中间节点,也就是翻转的头节点,这个在昨天的题目中讲到
|
|
|
|
|
//但是今天和昨天有一些不一样的地方就是,如果有两个中间节点返回第一个,昨天的题目是第二个
|
|
|
|
|
let midenode = searchmidnode(head);
|
|
|
|
|
//原地翻转链表,需要两个辅助指针。这个也是面试题目,大家可以做一下
|
|
|
|
|
//这里我们用的是midnode.next需要注意,因为我们找到的是中点,但是我们翻转的是后半部分
|
|
|
|
|
let backhalf = reverse(midenode.next);
|
|
|
|
|
//遍历两部分链表,判断值是否相等
|
|
|
|
|
let p1 = head;
|
|
|
|
|
let p2 = backhalf;
|
|
|
|
|
while (p2 != null) {
|
|
|
|
|
if (p1.val != p2.val) {
|
|
|
|
|
//若要还原,记得这里也要reverse
|
|
|
|
|
midenode.next = reverse(backhalf);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
p1 = p1.next;
|
|
|
|
|
p2 = p2.next;
|
|
|
|
|
}
|
|
|
|
|
//还原链表并返回结果,这一步是需要注意的,我们不可以破坏初始结构,我们只是判断是否为回文,
|
|
|
|
|
//当然如果没有这一步也是可以AC,但是面试的时候题目要求可能会有这一条。
|
|
|
|
|
midenode.next = reverse(backhalf);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//找到中点
|
|
|
|
|
var searchmidnode = function(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 reverse = function(slow) {
|
|
|
|
|
let low = null;
|
|
|
|
|
let temp = null;
|
|
|
|
|
while (slow != null) {
|
|
|
|
|
temp = slow.next;
|
|
|
|
|
slow.next = low;
|
|
|
|
|
low = slow;
|
|
|
|
|
slow = temp;
|
|
|
|
|
}
|
|
|
|
|
return low;
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Python Code:
|
|
|
|
|
|
|
|
|
|
```py
|
|
|
|
|
class Solution:
|
|
|
|
|
def isPalindrome(self, head: ListNode) -> bool:
|
|
|
|
|
if head is None or head.next is None:
|
|
|
|
|
return True
|
|
|
|
|
# 找到中间节点,也就是翻转的头节点,这个在昨天的题目中讲到
|
|
|
|
|
# 但是今天和昨天有一些不一样的地方就是,如果有两个中间节点返回第一个,昨天的题目是第二个
|
|
|
|
|
midnode = self.searchmidnode(head)
|
|
|
|
|
# 原地翻转链表,需要两个辅助指针。这个也是面试题目,大家可以做一下
|
|
|
|
|
# 这里我们用的是midnode.next需要注意,因为我们找到的是中点,但是我们翻转的是后半部分
|
|
|
|
|
backhalf = self.reverse(midnode.next)
|
|
|
|
|
# 遍历两部分链表,判断值是否相等
|
|
|
|
|
p1 = head
|
|
|
|
|
p2 = backhalf
|
|
|
|
|
while p2 is not None:
|
|
|
|
|
if p1.val != p2.val:
|
|
|
|
|
# 若要还原,记得这里也要reverse
|
|
|
|
|
midnode.next = self.reverse(backhalf)
|
|
|
|
|
print(head)
|
|
|
|
|
return False
|
|
|
|
|
p1 = p1.next
|
|
|
|
|
p2 = p2.next
|
|
|
|
|
# 还原链表并返回结果,这一步是需要注意的,我们不可以破坏初始结构,我们只是判断是否为回文,
|
|
|
|
|
当然如果没有这一步也是可以AC,但是面试的时候题目要求可能会有这一条。
|
|
|
|
|
midnode.next = self.reverse(backhalf)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
# 找到中点
|
|
|
|
|
def searchmidnode(self, head):
|
|
|
|
|
fast = head
|
|
|
|
|
slow = head
|
|
|
|
|
while fast.next is not None and fast.next.next is not None:
|
|
|
|
|
fast = fast.next.next
|
|
|
|
|
slow = slow.next
|
|
|
|
|
return slow
|
|
|
|
|
|
|
|
|
|
# 翻转链表
|
|
|
|
|
def reverse(self, slow):
|
|
|
|
|
low = None
|
|
|
|
|
temp = None
|
|
|
|
|
while slow is not None:
|
|
|
|
|
temp = slow.next
|
|
|
|
|
slow.next = low
|
|
|
|
|
low = slow
|
|
|
|
|
slow = temp
|
|
|
|
|
return low
|
|
|
|
|
```
|
|
|
|
|
|