判断某链表是否为回文结构

题目

给定一个链表的头节点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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值