从此篇博文开始,讲解一道古老的链表相交问题,共五篇
题目
给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交
解题步骤
- 判断两个【无环】链表是否相交
- 找到两个【无环】链表的相交结点
- 判断链表是否带环
- 判断两个【有环】链表是否相交
- 找到两个【有环】链表的相交结点
此篇先从最简单的判断两个【无环】链表是否相交开始,顺便介绍一下链表的基础知识,方便一些对链表不太了解的同学学习。
基础知识
什么是链表?
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
——From BaiKe
画个图展示一下链表
数据结构如下
struct ListNode{ int data; ListNode * nextNode; ListNode(ListNode * node,int value){ nextNode=node; data=value; } };
有环链表?
只需修改一下指针的指向,就会发现,这个链表永远不会走到尽头,如下
如何判断两个链表是否相交?
思路:只要有一个节点相同,那么两链表就相交
方法一: 遍历遍历
遍历链表一,每次遍历到链表一的一个节点时判断是否和链表二的节点相同(方法同样是遍历),有相同的则说明两链表相交。
此方法当然可行,可是时间复杂度=O(length1*length2)
方法二:哈希表法
既然连个链表一旦相交,相交节点一定有相同的内存地址,而不同的节点内存地址一定是不同的,那么不妨利用内存地址建立哈希表,如此通过判断两个链表中是否存在内存地址相同的节点判断两个链表是否相交。具体做法是:遍历第一个链表,并利用地址建立哈希表,遍历第二个链表,看看地址哈希值是否和第一个表中的节点地址值有相同即可判断两个链表是否相交。
时间复杂度O(length1 + length2)
空间复杂度O(length1) 因为需要创建大小为length1的哈希表
分析:时间复杂度是线性的,可以接受,并且可以顺便找到第一个相交节点,但是却增加了O(length1)的空间复杂度,这显然不能令人满意。——ref:http://www.cnblogs.com/BeyondAnyTime/archive/2012/07/06/2580026.html
方法三:比较尾结点
只要两链表相交,那么相交后的那一段肯定是一样的,也就意味着尾结点是一样的
时间复杂度O(length1 + length2)
空间复杂度O(0)
寻找尾结点的函数,很简单,就不解释了
/** 寻找尾结点 */ ListNode * getLastNode(ListNode * head){ if(head==NULL) return NULL; while(head->nextNode!=NULL){ head=head->nextNode; } return head; }
源代码
#include <stdio.h> #include<stdlib.h> #include <iostream> using namespace std; /** 1.判断两个【无环】链表是否相交 思路 判断尾节点是否相等 */ /** 链表结构体 */ struct ListNode{ int data; ListNode * nextNode; ListNode(ListNode * node,int value){ nextNode=node; data=value; } }; ListNode * L1; ListNode * L2; //遍历链表 void ScanList(ListNode * node){ while(NULL!=node){ cout<<node->data<<endl; node = node->nextNode; } } /** 寻找尾结点 */ ListNode * getLastNode(ListNode * head){ if(head==NULL) return NULL; while(head->nextNode!=NULL){ head=head->nextNode; } return head; } //测试无环相交 void testCross(){ ListNode * node = new ListNode(NULL,0); node = new ListNode(node,1); node = new ListNode(node,2); L1 = new ListNode(node,11); L1 = new ListNode(L1,12); L1 = new ListNode(L1,13); L2 = new ListNode(node,21); L2 = new ListNode(L2,22); L2 = new ListNode(L2,23); } //测试无环不相交 void testNotCross(){ L1 = new ListNode(NULL,11); L1 = new ListNode(L1,12); L1 = new ListNode(L1,13); L2 = new ListNode(NULL,21); L2 = new ListNode(L2,22); L2 = new ListNode(L2,23); } void main() { testCross(); //testNotCross(); ListNode * node1 = getLastNode(L1); ListNode * node2 = getLastNode(L2); if(node1==node2) cout<<"相交"<<endl; else cout<<"不相交"<<endl; system("pause"); }
既然知道两个【无环】链表相交,那么怎么找到相交结点,下一节,聊聊这个。