题目:
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
解答:
Approach #1: 随手写了一个,把链表变成了数组。然后就变成了怎样判断一个数组是回文数组,很容易用双指针解决。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int>nodes;
ListNode* p = head;
while (p) {
nodes.push_back(p->val);
p = p->next;
}
if (nodes.empty()) {
return true;
}
for (int i = 0, j = nodes.size() - 1; i < j; i++, j--) {
if (nodes[i] != nodes[j]) {
return false;
}
}
return true;
}
};
Approach #2: 我们可以通过一次遍历链表,得到它的中间节点mid,然后将后半段链表反转,再用前半段与后半段比对。
举个栗子:
如果链表的节点个数是奇数个:
偶数个的情况:
看起来像是要对奇数个节点和偶数个节点分别进行判断,然而实际上不需要:
后半段链表的头实际上是mid->next,由于我们还需要mid(请往下看),于是将后半段链表的头节点直接用一个变量backHalf表示;一个显然的事实:我们在比对前半段链表与后半段链表的时候,要将head与backHalf的值依次比对,并且同时移动这两个指针:
奇数个节点的时候:
偶数个节点的时候:
可以看出来循环的终止条件可以完全一致:backHalf为空结束。
这里还有两个小问题:
a. 怎么找中间节点?
用两个指针,一个fast,一个slow;fast步长为2,slow步长为1,当fast走到头时,slow就指着中间节点。
b. 如何反转链表?
看这里:https://blog.csdn.net/souloh/article/details/81062223
实现代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
ListNode* reverse(ListNode* head) {
if (!head) {
return nullptr;
}
else if(!head->next) {
return head;
}
ListNode* p1 = head;
ListNode* p2 = head->next;
while (p2) {
ListNode* temp = p2->next;
p2->next = p1;
p1 = p2;
p2 = temp;
}
head->next = nullptr;
return p1;
}
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode* mid = head;
ListNode* last = head;
if (!head || !head->next) {
return true;
}
while (last->next && last->next->next) {
mid = mid->next;
last = last->next->next;
}
ListNode* backHalf = reverse(mid->next);
while (backHalf) {
if (head->val != backHalf->val) {
return false;
}
head = head->next;
backHalf = backHalf->next;
}
return true;
}
};
2019.9.23更新:重新复习一遍,Java实现。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) {
return true;
}
if (head.next == null) {
return true;
}
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
//这个时候,如果链表为奇数个节点 1 -> 2 -> 1, 那么fast指向最后一个,slow指向正中间
//如果链表为偶数个节点 1 -> 2 -> 2 -> 1, 那么fast指向倒数第二个节点,slow指向正中间(前面那个)
//思路是将slow后面那个节点正好对应的后半段链表反转,然后比对
ListNode secondHalfHead = reverseList(slow.next);
while (secondHalfHead != null) {
if (head.val != secondHalfHead.val) {
return false;
}
head = head.next;
secondHalfHead = secondHalfHead.next;
}
return true;
}
private ListNode reverseList(ListNode head) {
if (head == null) {
return head;
}
ListNode p1 = head;
ListNode p2 = head.next;
while (p2 != null) {
ListNode temp = p2.next;
p2.next = p1;
p1 = p2;
p2 = temp;
}
head.next = null;
return p1;
}
}