单链表判断公共节点

单链表判断有无公共节点是个比较有趣的问题。这里所说的公共节点指的是完全相同的节点,不同与一般意义上的节点元素相同。相交单链表简单的都会是如下形式(有环除外):

粗略分析,容易想到就是暴力法,双重循环寻找公共节点。

关于单链表的判断有无公共节点,除了暴力法之外,还有很多方法可以尝试。下面简单列举几种。

可以尝试hash,如果两个节点的首地址相同,则该节点必定相同,可以以空间换取时间,先处理其中一个单链表,建立hash表,然后处理另外一个判断有无公共节点,时间复杂度为O(max(n,m)),空间复杂度为O(min(n,m))。

由于单链表的特殊性,若只需判断有无公共节点,可以直接比较最后一个节点,相同则有公共节点,不通则没有公共节点。

当然,可以尾部对齐,然后将较长单链表的指针移到与较短的单链表头指针一致的地方,之后判断两个指针是否相同,不同则同时后移;相同则证明有公共节点并且找到公共节点开始位置。此算法时间复杂度为O(max(n,m)),空间复杂度为O(1)。

还有另外一种较为好的方法,即将其中一个单链表接到另外一个的尾部,然后判断有无环。若两个单链表相交,则必然有环,环的入口就是交点;若无环,则必无公共节点。下面问题就转化为求单链表有无环且在有环情况下找到环的入口。这样做的前提是单链表无环。如图:

判断环可以类比追击问题,设置两个步长,然后看二者能否在出发后相遇,能则有环;否则无环。当然步长可以有多种组合,考虑到是离散量即相遇时不能取小数,所以去步长为1、2比较合适,这点在后面求环的入口是非常重要的。时间复杂度为O(n),空间复杂度为O(1)。

下面是求环的入口示意图:

两个单链表总得有如下3中可能组合(具体说明明见图):

下面贴段代码:

#include<iostream>
using namespace std;

struct SNode
{
	SNode *next;
	int key;
};

/*********************************
*参数:链表头结点引用
*功能:初始化单链表头结点
*返回值:空
**********************************/
void Inite(SNode *&tempHead)
{
	tempHead=new SNode;
	tempHead->next=NULL;
}

/*********************************
*参数:链表头结点
*功能:尾插法插入单个元素
*返回值:无
**********************************/
void Insert(SNode *tempHead,int tempKey)
{
	if(NULL==tempHead)return ;
	SNode *pre=tempHead,*cur=tempHead->next;
	while(cur!=NULL)
	{
		pre=cur;
		cur=cur->next;
	}
	SNode *p;
	p=new SNode;
	p->key=tempKey;
	p->next=NULL;
	pre->next=p;
}

/*********************************
*参数:链表头结点
*功能:判断单链表是否有环
*返回值:有环,true;否则,false
**********************************/
bool IsLoop(SNode *tempHead)
{
	if(NULL==tempHead||NULL==tempHead->next)
		return false;
	SNode *slow=tempHead->next,*fast=slow;
	while(fast!=NULL&&fast->next!=NULL)
	{
		slow=slow->next;
		fast=fast->next->next;
		if(slow==fast)return true;
	}
	return false;
}

/*********************************
*参数:链表头结点
*功能:计算单链表环的入口位置
*返回值:存在则返回环的入口位置,否则返回NULL
**********************************/
SNode* GetEntryLoopNode(SNode *tempHead)
{
	if(NULL==tempHead||NULL==tempHead->next)
		return NULL;
	SNode *slow=tempHead->next,*fast=slow;
	while(fast!=NULL&&fast->next!=NULL)
	{
		slow=slow->next;
		fast=fast->next->next;
		if(slow==fast)break;
	}
	if(NULL==fast&&NULL==fast->next)
		return NULL;
	slow=tempHead->next;
	while(slow!=fast)
	{
		slow=slow->next;
		fast=fast->next;
	}
	return fast;
}

/*********************************
*参数:链表头结点
*功能:计算链表长度
*返回值:链表长度
**********************************/
int GetLength(SNode *tempHead)
{
	int len=0;
	if(NULL==tempHead)
		return -1;
	SNode *p=tempHead->next;
	while(p!=NULL)
	{
		++len;
		p=p->next;
	}
	return len;
}

/*********************************
*参数:两链表头结点
*功能:计算两个单链表公共节点开始位置
*返回值:存在则返回开始位置,否则返回NULL
**********************************/
SNode* FirstComNode(SNode *tempHead1,SNode*tempHead2)
{
	int len1=GetLength(tempHead1);
	int len2=GetLength(tempHead2);
	if(-1==len1||-1==len2)
		return NULL;
	SNode *p,*q;//p指向较长者,q指向较短者
	cout<<"len1="<<len1<<"  len2="<<len2<<endl;
	//保证都从倒数第n个比较,n为len1,len2较小者
	if(len1>len2)
	{
		p=tempHead1->next;
		q=tempHead2->next;
		int tempLen=len1;
		while(tempLen>len2)
		{
			cout<<p->key<<"@    ";
			--tempLen;
			p=p->next;
		}
	}
	else
	{
		p=tempHead2->next;
		q=tempHead1->next;
		int tempLen=len2;
		while(tempLen>len1)
		{
			cout<<p->key<<"&    ";
			--tempLen;
			p=p->next;
		}
	}
	cout<<endl;
	while(p!=NULL&&q!=NULL&&p!=q)
	{
		p=p->next;
		q=q->next;
	}
	if(p==q)
		return p;
	return NULL;
}

/*********************************
*参数:两个链表头结点
*功能:判断单链表是否相交
*返回值:相交,true;否则,false
*注意:此时单链表结构改变了
**********************************/
bool IsHasComNode(SNode *tempHead1,SNode*tempHead2)
{
	if(NULL==tempHead1||NULL==tempHead1)
		return false;
	if(tempHead1->next!=NULL)
	{
		if(tempHead2->next!=NULL)
		{
			SNode *temp=tempHead1->next;
			tempHead1->next=tempHead2->next;
			SNode *cur=tempHead2;
			while(cur->next!=NULL)
				cur=cur->next;
			cur->next=temp;
		}
	}
	else tempHead1=tempHead2;
	return IsLoop(tempHead1);
}

int main()
{
	SNode *head1,*head2;
	Inite(head1);
	Inite(head2);
	for(int i=0;i<10;i++)
		Insert(head1,i);

	Insert(head2,20);
	head2->next->next=head1->next->next->next;//

	if(IsLoop(head1))
		cout<<"有环"<<endl;
	else cout<<"无环"<<endl;

	SNode * firComNode=FirstComNode(head1,head2);
	if(firComNode!=NULL)
		cout<<"最开始公共节点为:"<<firComNode->key<<endl;
	else cout<<"无公共节点"<<endl;

	if(IsHasComNode(head1,head2))
		cout<<"有公共节点"<<endl;
	else cout<<"无公共节点"<<endl;

	system("pause");
	return 0;
}

由于时间不足,缺乏辩证,如有错误,欢迎斧正!

单链表判断公共节点,布布扣,bubuko.com

时间: 2024-10-04 10:33:46

单链表判断公共节点的相关文章

从无头单链表中删除节点

1.从无头单链表中删除节点 一个没有头指针的单链表.一个指针指向此单链表中间的一个节点(不是第一个也不是最后一个节点).将该节点删除. A-->B-->C-->D       =====>     A-->C-->D 问题解析:由于只能单向遍历,故与删除B节点,无法得知B的前驱A,即无法像普通删除中那样让A的next指向C; 这里有个技巧,将C当做B,而B复制成C,则删除C节点即可: 2.给定一个链表的头指针,要求只遍历一次,将单链表中的元素顺序反转过来. A-->

快慢指针原理--快速找到未知长度单链表的中间节点

package com.java.dataStruct; //节点类 public class Node<E> { E item; Node next; public Node(){ } public Node(E element){ this.item = element; } public Node(E element, Node next){ this.item = element; this.next = next; } } Node p1,r1; Node L1 = new Node

求单链表的中间节点,用快慢指针

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Node* findMid(Node* &head) {     if(head == NULL||head->next == NULL)         return head;          Node* p = head;     Node* q = head;     while(q->next->next&&(q = q->next))     {         p = p-

C语言:【单链表】查找单链表的中间节点,要求只能遍历一次

#include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int DataType; typedef struct SListNode {     DataType data;     struct SListNode* next; }SListNode; SListNode* BuyNode(DataType x) {     SListNode* next = (SListNode*)malloc

Leetcode:Swap Nodes in Pairs 单链表相邻两节点逆置

Given a linked list, swap every two adjacent nodes and return its head. For example, Given 1->2->3->4, you should return the list as 2->1->4->3. Your algorithm should use only constant space. You may not modify the values in the list, on

单链表一[带头节点链表]

单链表实现分带头节点链表和不带头节点链表: 使用头文件如下: struct LinkNode {     void *x;      struct LinkNode *next; }; 一,带头节点的链表: 1,链表创建 程序说明: 1)函数调用形式:szyu_link_create0("AA", "BB", NULL);其中NULL结尾是在for循环的判断结束条件为x == NULL.使用NULL可以是for循环正常退出 2)程序先创建头节点head,并初始化he

C++算法之 找出两个链表的公共节点

题目:输入两个链表,找出它们第一个公共节点.链表节点定义如下: struct ListNode { int    m_nKey; ListNode*   m_pNext; } 方法1:在第一个链表上顺序遍历每一个节点,没遍历一个节点,在第二个链表上顺序遍历每个节点.  O(n^2) 方法2:找到两个链表的长度差,先遍历长链表到短链表的长度处,然后两个链表同时遍历,没遍历依次比较两个节点指针是否相同, 注意是比较节点指针,不是节点的值! 代码: // FindFirstCommandNode.cp

删除单链表中的节点

最近看到Linus在采访中提到一种删除单链表中节点的方法,并说不懂这种方法的人都不理解指针,原话如下: At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of poi

3.4 从无头单链表中删除节点

题目:有一个单链表,没有头指针,现在有一个指针指向这个链表的中间的一个节点,要求删除这个节点. 方法:由于没有办法寻求指针去找到要删除的这个节点的前一个节点. 所以要另想办法. 设立新的指针指向要删除的节点的后一个节点,然后把这两个节点里面的数据换一下!然后删除要删除节点的后一个节点! 这样 就ok了! 代码: void DeleteRandomNode(node *pCurrent) { assert(pCurrent != NULL); node *pNext = pCurrent->nex