1. 題目描述(基礎要求)
給定一個鏈表的頭節點head,請判斷該鏈表是否爲迴文結構。 例如: 1->2->1,返回true。 1->2->2->1,返回true。15->6->15,返回true。 1->2->3,返回false。
2. 思路
如果對空間複雜度沒有要求的話,我們可以藉助輔助空間.我們藉助一個STL的stack將我們的value放到棧當中,然後對比的時候從棧頂彈出元素,如果有一個不相等則返回false,否則返回true; 進一步的話我們可以使用二個指針,一個快指針,一個慢指針,快指針一次走二步,慢指針一次走一步,當快指針到達終點的時候,慢指針剛好到達中點,然後我們將整個鏈表分成左和右二個部分,然後我們將右半部分壓棧進行對比,這樣就會節省一半的空間.
3. 進階版本
如果程序進一步要求:如果鏈表長度爲N,時間複雜度達到O(N),額外空間複雜度達到O(1)。也就是不準使用輔助空間,這個時候怎麼辦了?
- 首先用一個快指針,一個慢指針來遍歷鏈表。快指針每次走兩步,慢指針每次走一步。等到快指針遍歷完鏈表,慢指針就正好停留在鏈表的中央。
- 將鏈表的後半部分進行反轉。
- 將鏈表的前半部分與後半部分逐個比對。若都相同,則爲迴文鏈表,否則不是。
- 將反轉之後右半部分的鏈表進行恢復.
4. 代碼
4.1 程序說明:
這裏我們實現了三個函數進行判斷是否爲迴文鏈表,分別是:
isPalindrome1
使用輔助空間,對應基礎版本.isPalindrome2
使用輔助空間,對應基礎版本.isPalindrome3
使用輔助空間,對應進階版本.
#include <iostream>
#include <vector>
#include <stack>
struct ListNode {
int val;
struct ListNode* next;
ListNode(int x) :
val(x), next(nullptr) {
}
};
// need n extra space
bool isPalindrome1(ListNode* head) {
std::stack<int> my_node_value;
ListNode* curNode = head;
while (curNode != nullptr) {
my_node_value.push(curNode->val);
curNode = curNode -> next;
}
while (head!= nullptr) {
if (head -> val != my_node_value.top()) {
return false;
} else {
head = head -> next;
my_node_value.pop();
}
}
return true;
}
// need n/2 extra space
bool isPalindrome2(ListNode* head) {
if (head == nullptr || head -> next == nullptr) {
return true;
}
std::stack<int> my_node_value;
ListNode* slowNode = head -> next;
ListNode* fastNode = head;
while (fastNode -> next != nullptr && fastNode -> next -> next != nullptr) { // get the middle node
slowNode = slowNode -> next;
fastNode = fastNode -> next -> next;
}
while (slowNode != nullptr) {
my_node_value.push(slowNode -> val);
slowNode = slowNode -> next;
}
while (!my_node_value.empty()) { // here we can't use the condition: head != nullptr
if (head -> val != my_node_value.top()) {
return false;
break;
} else {
head = head -> next;
my_node_value.pop();
}
}
return true;
}
// need O(1) extra space
bool isPalindrome3(ListNode* head) {
if (head == nullptr || head -> next == nullptr) {
return true;
}
ListNode* n1 = head; // slow pointer
ListNode* n2 = head; // fast pointer
while (n2 -> next != nullptr && n2 -> next -> next != nullptr) {
n1 = n1 -> next;
n2 = n2 -> next -> next;
}
n2 = n1 -> next; // n2 now is the first node in the second list
n1 -> next = nullptr;
ListNode* n3 = nullptr; // A ListNode to store the last node in the second list
while (n2 != nullptr) {
n3 = n2 -> next;
n2 -> next = n1;
n1 = n2;
n2 = n3;
}
n3 = n1; // here n1 is the last node in the second list, we need to store the last node to reconver
n2 = head; // compare the first and the second list
while (n1 != nullptr && n2 != nullptr) {
if (n1 -> val != n2 -> val) {
return false;
break;
} else {
n1 = n1 -> next;
n2 = n2 -> next;
}
}
n1 = n3 -> next; // here we recover the second list to the origin order
n3 -> next = nullptr; // the origin last node next poiter is nullptr
while (n1 != nullptr) {
n2 = n1 -> next;
n1 -> next = n3;
n3 = n1;
n1 = n2;
}
return true;
}
void printList(ListNode* pHead) {
while (pHead != nullptr) {
std::cout << pHead -> val << ",";
pHead= pHead -> next;
}
std::cout << "\n";
}
int main()
{
ListNode* pHead = new ListNode(1);
pHead -> next = new ListNode(2);
pHead -> next -> next = new ListNode(1);
std::cout << "origin list is:" << std::endl;
printList(pHead);
bool is_palindrome = isPalindrome3(pHead);
if (is_palindrome) {
std::cout << "is palindrome" << std::endl;
} else {
std::cout << "not palindrome" << std::endl;
}
return 0;
}
這裏的話我們列舉了二種情況:一種是1 2 1 結構,另外一種是 1 2 3 結構,我們都得到了正確的結果,這裏以isPalindrome3
函數爲例子.
實現
isPalindrome3
函數還是有點複雜的,這個節點的指向有點繞暈了.