题目
给定一个链表的头节点head,请判断该链表是否为回文结构。例如:
1->2->1,返回true。
1->2->2->1,返回true。
15->6->15,返回true。
1->2->3,返回false。
方案1
我们可以先弄一个栈,遍历链表将所有东西都压入到栈中,第二次遍历时,每遍历一个元素,我们就从栈中取一个元素比对,如果一样对比下一个元素,如果不一样直接返回false,遍历完整个链表都一样,返回true。额外空间复杂度是O(N)
实例代码
#include <iostream>
#include <forward_list>
#include <stack>
using namespace std;
bool IsPalindromeList(const forward_list<int> intList)
{
stack<int> intStack;
//将数据压入栈中
for (forward_list<int>::const_iterator cit = intList.cbegin(); cit != intList.cend(); cit++)
{
intStack.push(*cit);
}
// 每次循环从栈中拿出一个元素然后与链表中的元素比较
for (forward_list<int>::const_iterator cit = intList.cbegin(); cit != intList.cend(); cit++)
{
if (*cit != intStack.top())
{
return false;
}
intStack.pop();
}
return true;
}
int main(int argc, char ** argv)
{
forward_list<int> head;
head.push_front(1);
head.push_front(2);
head.push_front(3);
head.push_front(4);
head.push_front(1);
bool res = IsPalindromeList(head);
cout << res << endl;
system("pause");
return EXIT_SUCCESS;
}
方案2
我们可以设置两个指针,一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步,快指针走完的时候,慢指针来到中点,然后我们将后半部分开始压入到栈中。压入完成后,每次从栈中弹出一个与链表的前半段进行比较,如果相等,进行下一次比较,否则就返回false,如果都是相等的,那么返回true。
该流程额外空间复杂度仍然是O(N)的,但是常数项要比第一个方案好。
#include <iostream>
#include <forward_list>
#include <stack>
using namespace std;
class Node
{
public:
Node(int value)
:m_value(value)
,m_next(nullptr)
{
}
Node *m_next;
int m_value;
};
bool IsPalindromeList(Node *head)
{
if (head == nullptr || head->m_next == nullptr)
{
return true;
}
Node *fast = head;
Node *slow = head;
stack<int> intStack;
while (fast->m_next != nullptr && fast->m_next->m_next != nullptr)
{
fast = fast->m_next->m_next;
slow = slow->m_next;
}
slow = slow->m_next;
while (slow)
{
intStack.push(slow->m_value);
slow = slow->m_next;
}
slow = head;
while (!intStack.empty())
{
if (slow->m_value != intStack.top())
{
return false;
}
intStack.pop();
slow = slow->m_next;
}
return true;
}
int main(int argc, char ** argv)
{
Node head(1);
head.m_next = new Node(2);
head.m_next->m_next = new Node(3);
head.m_next->m_next->m_next = new Node(2);
head.m_next->m_next->m_next->m_next = new Node(1);
bool res = IsPalindromeList(&head);
cout << res << endl;
system("pause");
return EXIT_SUCCESS;
}
方案3
快指针一次走两步,慢指针一次走一步,从中点开始让右半部分逆序,然后判断左右两部分对应位置上元素时候一样,一样返回true,不一样返回false。不管一样不一样我们都应该将我们对于输入的修改再修改回去。额外空间复杂度是O(1)的,时间复杂度是O(N)。
建议:在笔试的时候用使用额外空间的解,而在面试的时候使用额外空间小的解。这样才能打动他。
示意图
原始输入如下
我们调整链表之后
从head1与head2处开始遍历,如果在遍历的过程中发现不一致情况,直接返回false,如果直到有任意一方为空都没有不一致现象,那么就返回true。
最后记得调整回去
示例代码
#include <iostream>
#include <forward_list>
using namespace std;
class Node
{
public:
Node(int value)
:m_value(value)
,m_next(nullptr)
{
}
Node *m_next;
int m_value;
};
bool IsPalindromeList(Node *head)
{
if (head == nullptr || head->m_next == nullptr)
{
return true;
}
Node *fast = head;
Node *slow = head;
while (fast->m_next != nullptr && fast->m_next->m_next != nullptr)
{
fast = fast->m_next->m_next;
slow = slow->m_next;
}
// 将右半部分逆序
Node * pre = slow;
slow = slow->m_next;
pre->m_next = nullptr; // 将左右两个链表断开
while (slow != nullptr)
{
Node *temp = slow->m_next;
slow->m_next = pre;
pre = slow;
slow = temp;
}
bool flag = true;
// 此时pre指向反向后链表的第一个节点
Node *reverseHead = pre; // 保留反向链表的头,后面恢复链表需要使用
slow = head;
while (pre != nullptr && slow != nullptr)
{
if (pre->m_value != slow->m_value)
{
flag = false;
}
pre = pre->m_next;
slow = slow->m_next;
}
// 将链表翻转回原来的样子。
pre = nullptr;
while (reverseHead != nullptr)
{
Node *temp = reverseHead->m_next;
reverseHead->m_next = pre;
pre = reverseHead;
reverseHead = temp;
}
return flag;
}
int main(int argc, char ** argv)
{
Node head(1);
head.m_next = new Node(2);
head.m_next->m_next = new Node(3);
head.m_next->m_next->m_next = new Node(3);
head.m_next->m_next->m_next->m_next = new Node(2);
head.m_next->m_next->m_next->m_next->m_next = new Node(1);
bool res = IsPalindromeList(&head);
cout << res << endl;
system("pause");
return EXIT_SUCCESS;
}