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

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
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值