【100题】第七题(单链表相交)

2024-04-05 02:32
文章标签 100 单链 相交 第七

本文主要是介绍【100题】第七题(单链表相交),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目:给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交,  为了简化问题,我们假设俩个链表均不带环。

           问题扩展:
           1,如果链表可能有环列?
           2,如果需要求出俩个链表相交的第一个节点列?

解答:

一,关于链表有环的思考

理解: 1,如何判断链表有环?

            思考:单链表有环的判断?什么意思?一直向后递归递归到p->next=NULL;不就完了?

            更正:如果一直向后递归,假如存在环的话,会出现死循环。

            方法:【一】:可以做标记的话,在第一个节点做上标记。若访问到标记节点则有环,若访问到NULL则无环。(这种方法不可取,只在所有节点构成一个整环时才可以

            下图:

                         

                       【二】:不可以做标记的话,设置两个节点slow,fast。slow每次走一步,fast每次走两步。如果slow指针跟fast指针重合则表示有环。

                       【三】:应该还有方法待思考……

           2,如何判断环的长度

                  记录下问题1的碰撞点p,slow、fast从该点开始,再次碰撞所走过的操作数就是环的长度s。

               3,如何找到环的入口在哪里?

                 有定理:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。(证明在后面附注)

          4,带环链表的长度是多少?

                 问题2求出环的长度,问题3求出头指针到连接点的距离。两者相加等于链表长度。

     程序源码:(待续)

void Isloop(Llink head)  //判断是否存在环,如果有环则输出(非环节点数,环节点数,总节点数)环开始节点
{if(!head||!head->next)return;Llink p,q;bool loop=false;p=q=head->next;while(q&&q->next)//判断是否有环{p=p->next;q=q->next->next;if(p==q){loop=true;break;}}if(!loop)cout<<"This link has not loop\n";else{cout<<"This link has a loop\n";Llink r=p;q=head->next;int nonloop=1,loopcount=1;//nonloop计算非环结点数,loopcount计算环上结点数do//计算环上的结点数{p=p->next;++loopcount;}while(p!=r);--loopcount;while(p!=q)//得到环的入口结点,同时计算得到非环的结点数{p=p->next;q=q->next;++nonloop;}--nonloop;cout<<"\nStart of loop: "<<p->data<<endl;  //环开始的地方 cout<<"\nCount of nonloop: "<<nonloop //非环的结点数<<"\nCount of loop: "<<loopcount//环的节点数 <<"\nCount of Linknode: "<<nonloop+loopcount<<endl;//总共的节点数 }
}
寻找环的入口的源码:

slist* FindLoopPort(slist *head)  //单纯的找环开始点 
{  slist *slow = head, *fast = head;    while ( fast && fast->next )   {  slow = slow->next;  fast = fast->next->next;  if ( slow == fast ) break;  }    if (fast == NULL || fast->next == NULL)  return NULL;  slow = head;  while (slow != fast)  {  slow = slow->next;  fast = fast->next;  }  return slow;  
} 
寻找环入口方法二:亦可以用类似与hash表的方法,即设立一个数组,将链表结点中的值做数组下标,当赋值冲突时就是环的接入点

bool isloop(Llink p)
{if(!p||!p->next)return true;int a[MAXSIZE],n=0;memset(a,0,sizeof(int)*MAXSIZE);p=p->next;while(p){if(a[p->data]==-1)//存在环时,会发生冲突{cout<<"\nLoop node: "<<p->data<<endl<<"\nLen of node: "<<n<<endl;return true;}a[p->data]=-1;++n;p=p->next;}return false;
}
创建一个有环的链表

Llink CreatlinkLoop()
//创建一个有环的链表
{Llink head=new Lnode;//head->data=0;head->next=NULL;Lelemtype e;Llink q=head;int N=0;cout<<"input elems:";while(cin>>e){Llink p=new Lnode;++N;p->data=e;p->next=q->next;q->next=p;q=p;}cin.clear();cin.sync();srand(time(0));q->next=Findnode(head,rand()%N);//随机产生环的接入点return head;
}
Llink Findnode(Llink head,int n)//找出链表中的第n个结点
{if(n<=0)return head;Llink p=head->next;for(int i=1;p&&i<n;++i)p=p->next;return p;
}

二,分析与解法

        A----B----C------D、

                                       、

                                         I-----J----NULL

                                       /

       E-----F-----G------H

        这样的一个问题,也许我们平时很少考虑。但在一个大的系统中,如果出现两个链表相交的情况,而且释放了其中一个链表的所有节点,那样就会造成信息的丢失,并且另一个与之相交的链表也会受到影响,这是我们不希望看到的。在特殊的情况下,的确需要出现相交的两个链表,我们希望在释放一个链表之前知道是否有其他链表跟当前这个链表相交。
      【解法一】直观的想法
                       看到这个问题,我们的第一个想法估计都是,“不管三七二十一”,先判断第一个链表的每个节点是否在第二个链表中。这种方法的时间复杂度为 O(Length(h1) * Length(h2))。可见,这种方法很耗时间。


     【解法二】利用计数的方法(hash表 最简单)
                      很容易想到,如果两个链表相交,那么这两个链表就会有共同的节点。而节点地址又是节点的唯一标识。所以,如果我们能够判断两个链表中是否存在地址一致的节点,就可以知道这两个链表是否相交。一个简单的做法是对第一个链表的节点地址进行 hash 排序,建立hash 表,然后针对第二个链表的每个节点的地址查询 hash 表,如果它在 hash 表中出现,那么说明第二个链表和第一个链表有共同的节点。这个方法的时间复杂度为 O(max(Length(h1)
+ Length(h2)))。但是它同时需要附加 O(Length(h1))的存储空间,以存储哈希表。虽然这样做减少了时间复杂度,但是是以增加存储空间为代价的。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?


     【解法三】链接“头尾”判断环法
                     由于两个链表都没有环,我们可以把第二个链表接在第一个链表后面,如果得到的链表有环,则说明这两个链表相交。否则,这两个链表不相交这样我们就把问题转化为判断一个链表是否有环。链表有环的情况判断一个链表是否有环,也不是一个简单的问题,但是需要注意的是,在这里如果有环,则第二个链表的表头一定在环上,我们只需要从第二个链表开始遍历,看是否会回到起始点就可以判断出来。最后,当然可别忘了恢复原来的状态,去掉从第一个链表到第二个链表表头的指向。这个方法总的时间复杂度也是线性的,但只需要常数的空间。
     【解法四】最后结点地址比较法
           仔细观察题目中的图示,如果两个没有环的链表相交于某一节点的话,那么在这个节点之后的所有节点都是两个链表所共有的。那么我们能否利用这个特点简化我们的解法呢?困难在于我们并不知道哪个节点必定是两个链表共有的节点(如果它们相交的话)。进一步考虑“如果两个没有环的链表相交于某一节点的话,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。

          先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为 O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法比解法三更胜一筹。



         【如果两个有环】判断第一个链,直链跟环交点为O1,判断第二个链,直链跟环交点为O2。

                                        比较O1跟O2地址,如果相等则相交,否则从O1遍历,每次与O2地址比较,直到回到O1。期间有相等的则相交,否则不想交。

 

 

这篇关于【100题】第七题(单链表相交)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/877431

相关文章

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

Go语言构建单链表

package mainimport "fmt"type ListNode struct {Val intNext *ListNode}func main() {list := []int{2,4,3}head := &ListNode{Val:list[0]}tail := head //需要头尾两个指针for i:=1;i<len(list);i++ {//方法一 数组直接构建链表tai

【LeetCode热题100】前缀和

这篇博客共记录了8道前缀和算法相关的题目,分别是:【模版】前缀和、【模版】二维前缀和、寻找数组的中心下标、除自身以外数组的乘积、和为K的子数组、和可被K整除的子数组、连续数组、矩阵区域和。 #include <iostream>#include <vector>using namespace std;int main() {//1. 读取数据int n = 0, q = 0;ci

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

[数据结构]线性表之单链表的类模板实现

类的具体实现如下: /#include"LinearList.h"#include <iostream>#include <cstdlib>using namespace std;template<class T>struct LinkNode //链表节点类{T data;LinkNode<T>* link;LinkNode(LinkNode<T>* ptr=NULL):

牛客小白月赛100(A,B,C,D,E,F三元环计数)

比赛链接 官方讲解 这场比较简单,ABC都很签到,D是个不太裸需要预处理的 B F S BFS BFS 搜索,E是调和级数暴力枚举,F是三元环计数。三元环考的比较少,没见过可能会偏难。 A ACM中的A题 思路: 就是枚举每个边变成原来的两倍,然后看看两短边之和是否大于第三边即可。 不能只给最短边乘 2 2 2,比如 1 4 8 这组数据,也不能只给第二短边乘 2 2 2,比

诺瓦星云校招嵌入式面试题及参考答案(100+面试题、10万字长文)

SPI 通信有哪些内核接口? 在嵌入式系统中,SPI(Serial Peripheral Interface,串行外设接口)通信通常涉及以下内核接口: 时钟控制接口:用于控制 SPI 时钟的频率和相位。通过设置时钟寄存器,可以调整 SPI 通信的速度以适应不同的外设需求。数据发送和接收接口:负责将数据从主机发送到从机以及从从机接收数据到主机。这些接口通常包括数据寄存器,用于存储待发

逆序和顺序创建单链表

单链表是一种顺序的存储方式,数据结构学的不好,考研又是必考内容,只好从头开始学习,相信不断地积累会有更好的爆发! 首先单链表的创建,单链表是建立在结构体的基础上,要创建单链表首先要建立起一个储存数据的结构体: struct node{int elem;node *next;};elem是数据域,用来存放你要输入的数据,next是指向下个存放数据节点的指针同为node 类型; 下面是

多个线程如何轮流输出1到100

多个线程如何轮流输出1到100的值 这个面试问题主要考察如何让线程同步,首先线程同步必会用到的就是互斥锁,互斥锁保证多个线程对数据的同时操作不会出错。但是线程同步还会用到条件变量condition_variable,condition_variable(条件变量)是 C++11 中提供的一种多线程同步机制,它允许一个或多个线程等待另一个线程发出通知,以便能够有效地进行线程同步。 conditi