面试算法 牛客题目 判断一个链表是否为回文结构

1.题目:  判断一个链表是否为回文结构
描述
给定一个链表,请判断该链表是否为回文结构。
回文是指该字符串正序逆序完全一致。
数据范围: 链表节点数 0 \le n \le 10^50≤n≤10 
 链表中每个节点的值满足 |val| \le 10^7∣val∣≤10 


2.算法:
//1.暴力算法:全部反转  
//2.双指针算法 半边链表反转 


算法思想:

1.暴力算法:新建一个相同的链表  反转他  比较值的大小

2.双指针算法 半边链表反转 :

  • 空间复杂度:O(n)O(n)O(n),记录链表元素的辅助数组

知识点:双指针

双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个指针(特殊情况甚至可以多个),两个指针或是同方向访问两个链表、或是同方向访问一个链表(快慢指针)、或是相反方向扫描(对撞指针),从而达到我们需要的目的。

思路:

在数组中,我们可以借助双指针,一个从前往遍历前一半数组,另一个从后往前遍历后一半数组,依次比较值。链表中如果我们要用这样的思想,左指针从前往后很容易,直接的链表的遍历就可以了。但是右指针是真的没有办法从尾巴往前走,要是链表后半段的指针是逆序的就好了。

怎么样能让链表后半段的指针反过来,将后半段链表整体反转不就行了吗?如果我们将后半段链表整体反转,那么相当于后半段就是从末尾指向中间,就可以实现后半段的逆序遍历——按照指针直接走就可以了。

具体做法:

  • step 1:遍历链表,统计链表的长度。
  • step 2:将长度除2,遍历这么多位置,找到链表的中点。
  • step 3:从中点位置开始,对链表后半段进行反转。
  • step 4:与方法二类似,双指针左指针指向链表开头,右指针指向链表尾,此时链表前半段是正常的,我们也可以正常遍历,但是链表后半段所有指针都被我们反转逆序,因此我们可以从尾节点往前遍历。
  • step 5:依次比较对应位置的元素值是否相等。

3.双指针找中点(推荐使用)

思路:

上述方法三找中点,我们遍历整个链表找到长度,又遍历长度一半找中点位置。过程过于繁琐,我们想想能不能优化一下,一次性找到中点。

我们首先来看看中点的特征,一个链表的中点,距离链表开头是一半的长度,距离链表结尾也是一半的长度,那如果从链表首遍历到链表中点位置,另一个每次遍历两个节点的指针是不是就到了链表尾,那这时候我们的快慢双指针就登场了:

具体做法:

  • step 1:慢指针每次走一个节点,快指针每次走两个节点,快指针到达链表尾的时候,慢指针刚好到了链表中点。
  • step 2:从中点的位置,开始往后将后半段链表反转。
  • step 3:按照方法三的思路,左右双指针,左指针从链表头往后遍历,右指针从链表尾往反转后的前遍历,依次比较遇到的值


4.代码:

/*************************************************
作者:She001
时间:2022/9/30
题目:  判断一个链表是否为回文结构
描述
给定一个链表,请判断该链表是否为回文结构。
回文是指该字符串正序逆序完全一致。
数据范围: 链表节点数 0 \le n \le 10^50≤n≤10 
 链表中每个节点的值满足 |val| \le 10^7∣val∣≤10 



***************************************************/
//算法:
//1.暴力算法:全部反转  
//2.双指针算法 半边链表反转 

#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
	int i;
	node *next;	
}; 


void print(node * head)//打印链表 
{
	node* pp= head;//复制头节点 
	while(pp!=NULL)//判断这个节点是否为空  链表是否结束 
	{
		cout<<pp->i<<"  ";
		pp=pp->next;//指向下一个 
	}
	cout<<endl;
	
}



int lianbiao_num(node * head)//函数的作用 返回链表的个数
{
	int i=0;
	node* pp=head;
	while(pp!=NULL)
	{
		i++;
		pp=pp->next;	
	}
	//cout<<"链表中节点的个数: "<<i<<endl;
	return i;	
} 


node * reverseList(node* head)//翻转链表 
{
	if(head==NULL)
	{
		return NULL;	
	}	
	node * a = head;
	node * b = NULL;
	while(a!=NULL)
	{
		node * c = a->next;
		a->next=b;
		b=a;
		a=c;
	}
	return b;	
} 


//1.暴力算法:全部反转  
//
bool  fangfa_1(node * head)
{
	if(head==NULL)//链表为空返回 是回文 
	{
		return true;
	}
	if(lianbiao_num(head)==1)//链表只有一个 返回是回文 
	{
		return true;
	}
	node* a1=head;//指针遍历 链表 
	node* head2=new node;//建立的另外节点的头节点 
	node* hh=head2;//第二个节点的指针 方便连接 
	while(a1!=NULL)//建立第二个链表 
	{
		hh->i=a1->i;//第二个链表赋值 
		a1=a1->next;//指向下一个节点 
		if(a1==NULL)//判断下一个 节点是否空 
		{
			hh->next=NULL;
		}
		else
		{
			hh->next = new  node;
			hh=hh->next;
		}
	}
	
	node * h1= reverseList(head2);//翻转第二个链表 
	hh=h1; 
	a1=head;
	while(hh!=NULL && a1!=NULL)
	{
		if((hh->i) != (a1->i))
		{	
			while(h1!=NULL)//销毁建立的链表 
			{
				node * p=h1;
				h1=h1->next;
				delete p;
			}
			return false;
		}
		hh=hh->next;
		a1=a1->next;
	}
	
	while(h1!=NULL)//销毁建立的链表 
	{
		node * p=h1;
		h1=h1->next;
		delete p;
	}
	return true;
}



//2.双指针算法 半边链表反转 
bool  fangfa_2(node* head)
{
	if(head==NULL)//链表为空返回 是回文 
	{
		return true;
	}
	if(lianbiao_num(head)==1)//链表只有一个 返回是回文 
	{
		return true;
	}
	node * a1=head;
	node * a2=head;
	//双指针寻找中点
	while(a2!=NULL  && a2->next!=NULL)//这里考虑了  单数的情况 所以    123454321  会  a1 -> 5     ||||  12345 54321   a1-> 第二个 5  
	{
		a1=a1->next;
		a2=a2->next->next;	
	} 
	a1=reverseList(a1);//翻转中间到后面的代码   
	a2=head;//等于头部代码
	//链表的情况 
	// 1 -> 2-> 3->  4->  5 <-4 <-3<- 2 <-1
	// 1-> 2 -> 3 -> 4 -> 5 ->5 <- 4<- 3 <- 2 <-1
	while(a1!=NULL)//判断是否回文 
	{
		if((a1->i)!=(a2->i))
		{
			return false;
		}
		a1=a1->next;
		a2=a2->next;
	 }
	 return true; 
	
}  



int main()
{
	
	
	//建立  第一个 单链表  
	node *a1=new node;
	node *a2=new node;
	node *a3=new node;
	node *a4=new node;
	node *a5=new node;
	node *a6=new node;
	node *a7=new node;
	node *a8=new node;
	node *a9=new node; 
	
	a1->i=2;//链表节点的复制 
	a2->i=1;
	a3->i=4;
	a4->i=3;
	a5->i=7;
	a6->i=8;
	a7->i=9;
	a8->i=6;
	a9->i=5;
	
	
	a1->next=a2;//链表的连接 
	a2->next=a3;
	a3->next=a4;
	a4->next=a5;
	a5->next=a6;
	a6->next=a7;
	a7->next=a8;
	a8->next=a9;//a5 是 两个链表的节点 
	a9->next=NULL;
	
	//建立  第二个 单链表  
	node *b1=new node;
	node *b2=new node;
	node *b3=new node;
	node *b4=new node;
	node *b5=new node;
	node *b6=new node;
	node *b7=new node;
	node *b8=new node;
	node *b9=new node; 
	
	b1->i=1;//链表节点的复制 
	b2->i=2;
	b3->i=3;
	b4->i=4;
	b5->i=5;
	b6->i=4;
	b7->i=3;
	b8->i=2;
	b9->i=1;
	
	
	b1->next=b2;//链表的连接 
	b2->next=b3;
	b3->next=b4;
	b4->next=b5;
	b5->next=b6;
	b6->next=b7;
	b7->next=b8;
	b8->next=b9;//a5 是 两个链表的节点 
	b9->next=NULL;
	
/*
	int a=fangfa_1(a1);
	if(a==1)
	{
		print(a1); 
		cout<<"这个链表是回文!"<<endl;
	}
	else
	{
		print(a1); 
		cout<<"这个链表不是回文!"<<endl; 
	}
	
	int b=fangfa_1(b1);
	if(b==1)
	{
		print(b1);
		cout<<"这个链表是回文!"<<endl;
	}
	else
	{
		print(b1);
		cout<<"这个链表不是回文!"<<endl; 
	}
	
	
*/	
	int a=fangfa_2(a1);
	if(a==1)
	{
		print(a1); 
		cout<<"这个链表是回文!"<<endl;
	}
	else
	{
		print(a1); 
		cout<<"这个链表不是回文!"<<endl; 
	}
	
	int b=fangfa_2(b1);
	if(b==1)
	{
		print(b1);
		cout<<"这个链表是回文!"<<endl;
	}
	else
	{
		print(b1);
		cout<<"这个链表不是回文!"<<endl; 
	}

	return 	0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用快慢指针找到链表中点,然后将链表的后半部分反转,最后比较前半部分和反转后的后半部分是否相等即可。 具体步骤如下: 1. 定义快慢指针,将快指针每次移动两个节点,慢指针每次移动一个节点,直到快指针到达链表末尾,此时慢指针指向链表中点。 2. 反转链表的后半部分,从中点开始,使用三个指针依次将链表节点反转。 3. 比较前半部分和反转后的后半部分是否相等,如果相等,则链表回文结构,返回1,否则返回0。 代码实现如下: ### 回答2: 要判断一个链表是否回文结构,可以使用双指针法和栈来实现。 首先,使用快慢指针找到链表的中间节点。慢指针每次移动一个节点,快指针每次移动两个节点,当快指针到达链表末尾时,慢指针指向链表的中间节点。 然后,将链表后半部分的节点压入栈中。从中间节点的下一个节点开始,依次将节点压入栈中,直到快指针到达链表末尾。 最后,从链表的头节点开始,与栈中的节点进行比较。依次弹出栈中的节点,与当前链表节点比较,如果不相等,则该链表不是回文结构,返回0;如果所有节点都相等,则该链表回文结构,返回1。 以下是具体的代码实现: ```python class ListNode: def __init__(self, x): self.val = x self.next = None def isPalindrome(head): if not head or not head.next: return 1 # 快慢指针找到链表的中间节点 slow = fast = head while fast.next and fast.next.next: slow = slow.next fast = fast.next.next # 将后半部分的节点压入栈中 stack = [] cur = slow.next while cur: stack.append(cur) cur = cur.next # 从链表头节点开始与栈中的节点进行比较 cur = head while stack: if cur.val != stack.pop().val: return 0 cur = cur.next return 1 ``` 这样我们就可以通过调用isPalindrome函数,判断一个链表是否回文结构。返回1表示是回文结构,返回0表示不是回文结构。 注意,上述代码是基于Python语言的实现,对于其他语言可能需要稍作调整。 ### 回答3: 要判断一个链表是否回文结构,可以使用快慢指针找到链表的中间节点,并将后半部分反转,然后逐个比较前半部分和反转后的后半部分是否相等。 具体步骤如下: 1. 定义快慢指针slow和fast,初始时都指向链表的头部。 2. 使用快慢指针找到链表的中间节点,当fast指针到达链表末尾时,slow指针刚好指向链表中间节点。 3. 反转slow指针之后的链表。 4. 分别使用两个指针分别指向链表的头部和反转后的链表的头部。 5. 逐个比较两个链表中的节点值,如果有不相等的节点,则不是回文结构,输出0;否则,是回文结构,输出1。 下面是具体实现的代码: ```python class ListNode: def __init__(self, x): self.val = x self.next = None def reverseList(head): prev = None curr = head while curr: next_node = curr.next curr.next = prev prev = curr curr = next_node return prev def isPalindrome(head): if not head or not head.next: return 1 slow, fast = head, head while fast.next and fast.next.next: slow = slow.next fast = fast.next.next second_half = reverseList(slow.next) p1, p2 = head, second_half while p1 and p2: if p1.val != p2.val: return 0 p1 = p1.next p2 = p2.next return 1 ``` 时间复杂度分析:快慢指针遍历链表的时间复杂度为O(n),反转后半部分链表的时间复杂度为O(n/2),遍历比较两个链表的时间复杂度为O(n/2),所以总时间复杂度为O(n)。 空间复杂度分析:反转后半部分链表时需要使用一个额外的指针来储存当前节点的下一个节点,所以空间复杂度为O(1)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值