这一块主要记录一下链表相关的一些常考题目以及解题思路。
基本知识 这一块的难点主要是考察代码的实现能力,链表相关的题目在思路上比较简单,链表上每一个元素都存储空间都是临时分配的,每一个节点在存储空间的物理位置上是不一定连续的。链表的问题,在实现的时候,最优解往往不是使用额外的数据结构,这往往需要更多的技巧(最基本的比如链表的翻转)
在分类上
按照链表链接的方向,可以分为单链表以及双链表。对于单链表,每个节点只能通过其next指针指向下一个节点,双链表的每个节点,除了next指针之外,还有一个previous指针,可以指向它的前一个节点,通过当前节点,既可以找到前一个节点,也可以找到后一个节点。
按照有环以及无环分类,又可以分成普通链表(无环)以及循环链表(有环),循环链表是首尾相接的链表,循环链表的最后一个元素的next指针会指向其第一个元素,循环链表的第一个元素的previous指针会指向其最后一个元素。
实现上的注意问题
链表调整函数的返回值类型,根据要求,往往是节点的类型
处理链表的过程中,需要采用画图的方式,来使得整个逻辑尽量清晰
边界条件的处理要严格(头结点 尾节点 空节点)
链表插入和删除的注意事项
特殊处理链表为空,或者链表长度为1的情况
注意插入和删除的时候,要找到插入位置和删除位置的前一个节点,在头尾部进行插入和删除操作的时候需要特殊考虑
双链表的时候还需要考虑previous指针的情况
单链表翻转的操作(需要同时记录 previous节点 now节点 next节点 三个位置)
用临时变量记录下now节点的下一个节点的位置
now节点的next指针指向前一个节点 now节点变为新的head节点
循环以上操作,最后返回最新的head节点
实现上的注意事项
listnode类的题目,总是做起来不爽,因为要自己定义结构体还有组链表什么的。其实还是有点不熟悉。
自己定义结构体,注意初始化的函数。
typedef struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }ListNode;
还有就是初始化的时候 申请空间 给初始的空间赋值这些操作ListNode *h=(ListNode*)malloc(sizeof(ListNode)); h->next=NULL;
(事实上 这里的初始化赋值的操作完全可以根据链表的定义来进行,在定义的时候,就把初始化的操作写清楚了)
注意根据题目的要求,看链表在定义的时候究竟需不需要包含头结点,一般头结点就放在那里不动了,之后设置一个temp指针,来不断向后移动,用于执行具体的操作。
关于退出条件以及一些细节,在进行h->next这种操作之前,一定要判断是否h指针是否为NULL,要不然总会在莫名奇妙的地方出现问题。
一些常用的tips
这块的操作确比较繁琐,在初始话的时候最好使用构造函数的形式head=new ListNode(val);以免造成忘记初始化为NULL
head指针太关键了,所有操作也都是从head指针开始的,不管是循环的还是非循环的,都要保存一个head指针,根据题目的实际情况判断头结点设置为空节点,还是第一个元素所在位置的节点。(比如后面的删除值为val的节点的题目)
注意保存lastNode,即是当前节点的上一个节点,许多操作都是lastNode和currentNode结合起来进行的
插入,删除的时候,改变指针也都是先改变后面的再改变前面的
链表遍历的注意事项
为何链表的题目容易做错,既然思路不难,关键就在规范性上,在细节上面。比如遍历链表的操作,模式很简单,但是总是忘记tail指针,直接每次去拿头节点遍历,然后发现,要保存head指针,之后又调,总之,大多数的时候都是应为细节注意不到位带来的错误,还有比如链表节点的初始化等等。在这里规范一下遍历的思路。
//给定了链表的头指针 head //这里一定要注意 遍历的时候是用另外一个tail指针进行 头节点指针保持不变 ListNode *tail=head; while(tail!=NULL){ //do sth (create or some operations) for tail node tail=tail->next; }
在实际实现的时候,有好多题目会借用到两指针的相关思路
有序的环形单链表中插入新的元素 用两个index保存有当前元素和下一个元素的位置,开始的时候,一个在头结点,一个在第一个位置,之后两个指针同时向后移动,要是发现了合适的位置(insert值比前一个大 比后一个小)就插入元素。要是没有发现,就将最终的值插入到头结点和尾节点之间,此时插入的元素可能是所有元素里面最小的,也有可能是所有元素里面最大的。
如果插入元素是所有里面最小的,还是返回当前这个元素的位置,如果是所有元素里面最大的,返回原来头结点的位置。
这个题目在做的时候,总是报您的程序打印了太多的内容,暂时不知到错在哪里(估计还是哪里有死循环???),以下是源码:
#include <iostream> #include <vector> #include <string> #include <cstdio> using namespace std; struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; class InsertValue { public: ListNode* insert(vector<int> A, vector<int> nxt, int val) { // write code here int i,len,num; len=A.size(); int nxtIndex=0; int currVlue; ListNode *lastNode=NULL; ListNode *currNode=NULL; ListNode *head=NULL; if (len==0) { head=new ListNode(val); head->next=head; return head; } //lastNode could be the head node head=new ListNode(-1); lastNode=head; nxtIndex=0; num=0; while(num<len) { currVlue=A[nxtIndex]; currNode=new ListNode(currVlue); lastNode->next=currNode; lastNode=currNode; num++; nxtIndex=nxt[nxtIndex]; } //could be a circle if(nxtIndex==0) { currNode->next=head->next; } //debug ListNode *temp; temp=head; for(i=0; i<num+num; i++) { printf(" %d",temp->val); temp=temp->next; } putchar('\n'); //debugok lastNode=head->next; currNode=head->next->next; bool ifInsert=false; while(true) { if (currNode->next==NULL) { break; } //problem here //same value but different address?? if (currNode->next==head->next) { break; } if((lastNode->val<=val)&&(currNode->val>=val)) { ListNode *insertNode=new ListNode(val); lastNode->next=insertNode; insertNode->next=currNode; ifInsert=true; break; } lastNode=lastNode->next; currNode=currNode->next; } if (ifInsert) { return head->next; } if(val>currNode->val) { ListNode *insertNode=new ListNode(val); insertNode->next=currNode->next; currNode->next=insertNode; return head->next; } else { ListNode *insertNode=new ListNode(val); insertNode->next=head->next; head->next=insertNode; if(currNode->next!=NULL) { currNode->next=insertNode; } return head->next; } } }; int main() { int A[10]= {1,3,4,5,7}; int nxt[10]= {1,2,3,4,0}; vector<int> va; vector<int> vnxt; int i; for(i=0; i<5; i++) { va.push_back(A[i]); vnxt.push_back(nxt[i]); } InsertValue iv=InsertValue(); //debug ListNode *temp; ListNode *head; head=iv.insert(va,vnxt,0); int num=5; temp=head; for(i=0; i<num+num+num; i++) { printf(" %d",temp->val); temp=temp->next; if (temp==head){ break; } } //debugok return 0; }
删除链表中的节点(简单但是不容易想到 有种跳跃思维在里面) 给定一个链表中的节点node,但不给定整个链表的头结点,如何在链表中删除node? 只有一些简单的替代思路,暂时还没有更好的办法。
替代思路: 如果是双链表的情况,会比较容易处理,直接根据当前节点的preious以及next指针找到前一个元素以及后一个元素,之后把这两个元素的指针进行对应的调整,可以删除掉当前元素。
如果是单链表的情况,比如 1->2->3 这种情况,比如要删除值为2的节点,此时可以用节点3的值覆盖掉节点2之后把节点3删除,这仅仅是逻辑上的删除,不是真正意义上的删除,因为这种操作是通过值拷贝来进行的。
这种操作的问题是,无法删除最后一个节点,比如无法删除3号位置的节点。而且在实际工程中,每个节点所代表的意义并不相同,比如每个节点如果代表一个一个的服务器,这种copy可能无法把全部的依赖都copy过去,比如节点3的环境可能无法完全在节点2上重现,这个时候,把节点3删除了会带来未知问题。
代码实际上比较trick,仅仅是一种模拟的情况
class Remove { public: bool removeNode(ListNode* pNode) { // write code here if(pNode->next==NULL){ return false; } pNode->val=pNode->next->val; pNode->next=pNode->next->next; return true; } };
调整链表元素的位置(partition) 给定一个链表的头结点head,再给定一个数字num,请把链表调整成节点值小于num的节点都放在链表的左边,值等于num的节点都放在链表的中间,值大于num的节点都放在链表的右边。(虽然比较简单 还是一下想不到 多少有点豁然开朗的感觉)
考察的重点代码实现不出错的能力
思路一: 使用额外的数组,将链表中的元素放到数组中。之后类似荷兰国旗问题,对数组进行一次partition,之后再还原为链表。
思路二: 思路一其实没有发挥链表的优势:拆分灵活,实质上只需要在遍历链表的时候,把原来的大链表拆分成三个小的链表就可以了,拆分之后再讲这三个小的链表串起来,灵活方便,也不容易出错。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; class Divide { public: int ifempty(ListNode *head) { if (head->next==NULL) { return true; } else { return false; } } ListNode* listDivide(ListNode* head, int val) { ListNode *heada=new ListNode(-1); ListNode *headc=new ListNode(-1); ListNode *taila=heada; ListNode *tailc=headc; ListNode *returnHead; ListNode *temp=head; if (temp->next==NULL){ return head; } while(temp!=NULL) { ListNode *newnode=new ListNode(temp->val); if(temp->val<=val) { taila->next=newnode; taila=newnode; } else { tailc->next=newnode; tailc=newnode; } temp=temp->next; } if (ifempty(heada)==false) { returnHead=heada->next; taila->next=headc->next; } if(ifempty(heada)==true && ifempty(headc)==false) { returnHead=headc->next; } return returnHead; } }; int main() { int a[5]= {360,220,2}; int val=2; int i; ListNode *head=new ListNode(360); ListNode *tail=head; ListNode *returnHead; for(i=1; i<3; i++) { ListNode *temp=new ListNode(a[i]); tail->next=temp; tail=temp; } tail=head; while(tail!=NULL) { printf(" %d",tail->val); tail=tail->next; } putchar('\n'); Divide d; returnHead=d.listDivide(head,val); tail=returnHead; while(tail!=NULL) { printf(" %d",tail->val); tail=tail->next; } return 0; }
两个链表的公共部分(这个没有要求公共部分连续 降低了一些难度) 给定两个有序链表的头结点head1以及head2,求两个有序链表的公共部分。
其实思路也很简单,就是类似于之前的两指针的思路。
是指上是两指针问题的一个链表版本的实现。
首先是前提条件判断,如果为空的时候,就直接返回空,之后index1与index2都从头开始往后遍历,如果两个元素的值相等,则打印,之后两个index全都后移,否则,值稍微小一点的那个链表的index向后移动,重复前面的逻辑。
代码实现起来倒是比较简单,注意开始时候的空值判断
class Common { public: vector<int> findCommonParts(ListNode* headA, ListNode* headB) { vector<int> common; if (headA->next==NULL||headB->next==NULL) { return common; } ListNode *tailA=headA; ListNode *tailB=headB; while(tailA!=NULL&&tailB!=NULL) { if(tailA->val==tailB->val){ common.push_back(tailA->val); tailA=tailA->next; tailB=tailB->next; continue; }else if(tailA->val<tailB->val){ tailA=tailA->next; continue; }else{ tailB=tailB->next; continue; } } return common; } };
链表局部逆序(比较灵活 综合性较强) 给定一个链表的头结点head,实现一个调整单链表的函数,使得每K个节点之间逆序,如果最后不够K格节点一组,则不调整最后的几个节点。
比如链表 1->2->3->4->5->6->7->8->null K=3 进行调整之后,就会变成3->2->1->6->5->4->7->8->null 因为最后两个元素的个数加起来没有组成3,所以最后两个节点不够一组。 说起来简单做起来难,总之有好多边界情况需要考虑,这块可能隐藏着的坑比较多。
思路一: 其实有点综合题的感觉,涉及到逆序类的题目,可以很自然的联想到逆序的操作,比如前面K个元素先入栈,之后再反向一一弹出,每次弹出的时候,需要链表的next指针进行重连,具体涉及到的细节比较多。
注意最后元素不足k个的时候,应该如何处理。 组与组之前需要经行连接,需要记下上一组中的最后一个元素。 返回调整时候链表的头结点,函数应该返回链表的头结点。
思路二: 与方法一类似,就是省去了栈的这种额外结构,依然是搜集到k个元素就进行一次k个元素之间的逆序操作。每组逆序之后的头节点和上一组已经调整好的尾节点相连。
总的来说,使用栈的话,实现上还是会简单一些,下面是相关代码,做题的时候还是要仔细一点,别把stack与vector两个结构给弄混了。。。:
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using namespace std; struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; class KInverse { public: ListNode* inverse(ListNode* head, int k) { ListNode *tail=head; ListNode *newhead=new ListNode(-1); ListNode *newtail=newhead; stack<ListNode*> help; while(tail!=NULL) { help.push(tail); tail=tail->next; if (help.size()==k) { //pop and link to new list while(help.empty()==false) { ListNode *temp=help.top(); help.pop(); newtail->next=temp; newtail=newtail->next; } } } //extra element in stack ListNode *lastone; if(help.empty()==false){ lastone=help.top(); help.pop(); lastone->next=NULL; } while(help.empty()==false) { ListNode *temp=help.top(); help.pop(); temp->next=lastone; lastone=temp; } newtail->next=lastone; return newhead->next; } }; int main(){ int a[10]={1,2,3,4,5,6,7,8}; ListNode *head=new ListNode(a[0]); int i; ListNode *tail=head; for (i=1;i<8;i++){ ListNode *temp=new ListNode(a[i]); tail->next=temp; tail=tail->next; } tail=head; while(tail!=NULL){ printf(" %d",tail->val); tail=tail->next; } putchar('\n'); KInverse k; ListNode *newhead=k.inverse(head,3); tail=newhead; while(tail!=NULL){ printf(" %d",tail->val); tail=tail->next; } return 0; }
删除值为val的节点 给定一个单链表的头节点head,链表中的每个节点保存一个整数,再给定一个值val,把左右值等于val的节点全部删掉。(注意边界条件 仅有一个节点的时候怎么处理)
思路上不难,把整个过程看做是一个新链表的构造过程,还是细节,如果当前的这个节点是val,就直接抛弃,否则就接到新节点的末尾。
注意设置专用头结点的小技巧: 这个题目在给的时候,头结点的位置实际上是第一个元素,这种问题给法在实际处理的时候很不方便,应为总是要考虑第一个元素的特殊情况,后面case的处理方式无法和第一个统一起来,这个时候可以对原来的链表进行适当的“扩展”,就是扩展出一个realHead,作为真正的头结点,让realHead的next值指向第一个存储了元素的节点,即head,prev指针开始的时候就放在realHead的这个位置上,这样后面的情况处理起来就可以用一个逻辑进行了。
class ClearValue { public: ListNode* clear(ListNode* head, int val) { // write code here ListNode *realHead=new ListNode(-1); realHead->next=head; ListNode *prev=realHead; ListNode *curr=head; int temp; while(curr!=NULL){ temp=curr->val; if(temp==val){ prev->next=curr->next; delete(curr); curr=prev->next; }else{ curr=curr->next; prev=prev->next; } } return realHead->next; } };
判断一个链表是否为回文结构(后面两种方法很有启发性) 比如:链表 1-2-3-2-1 是回文结构,返回true,链表1-2-3-1不是回文结构,返回false。
思路1
借用栈,第一次遍历,把元素压入栈中,第二次遍历,栈中元素同时也向外弹出,一次比较,如果比较到最后,所有的元素都相等,说明是回文结构。(实现上是最简单的) 代码如下,注意stack的基本操作:
class Palindrome { public: bool isPalindrome(ListNode* pHead) { // write code here stack<ListNode*> s; ListNode*tail=pHead; while(tail!=NULL){ s.push(tail); tail=tail->next; } tail=pHead; ListNode*temp = NULL; while(tail!=NULL){ temp=s.top(); if (temp->val!=tail->val){ return false; } tail=tail->next; s.pop(); } return true; } };
思路2
思路1的升级版本,采用快指针和慢指针两个指针,慢指针一次向后移动一个元素,快指针一次向后移动两个元素,慢指针每次移动到的元素入栈。当快指针移动到末尾的时候,如果链表的个数奇数,就不入栈,如果为偶数则入栈,之后慢指针再向后移动,同时弹出栈中元素,依次比较是否相等,判断是否为回文链表。相当于是把链表左半部分折过来与链表的右半部分进行比较。
思路3
找到链表中间的位置,之后把链表右半部分逆序,之后采用两指针的思路,从链表的两端向中间的位置遍历,链表将被调整成这样的结构:1->2->3<-2<-1
random指针(较难 链表的拆分 拷贝) 一个链表结构中,每个节点不仅含有一条指向下一个节点的next指针,同时含有一条random指针,random指针可能指向链表当中的任意一个节点(可前可后),请复制这种含有random指针节点的链表。
这个题目的难点在与random指针的复制,比如新copy了一个节点,这个时候,如何让新节点的random指针指过去的元素也是新copy出来的,这是这个题目的难点所在。
思路:
遍历链表,每次遍历的时候,复制一个元素在后面,比如原先的链表为1->2->3,复制操作之后变为1->1’->2->2’->3->3’,这种结构的一个特殊性,或者说是规律性就在与,新copy出来的元素的位置在原来的老元素的下一个位置(这一点是相当关键的)
之后开始指针的复制,通过1’的random指针,可以找到原来1节点指向的元素位置,比如说是3,那么3的下一个位置就是3’,这个时候,就可以找到3’,将1’的random指针调整过去,依次这样进行,就可以完成所有的random指针的调整。
最后一步就会把就的链表和新的链表进行拆分处理,拆成两个新的链表。 下面是代码,不知道为内存超限了,注意一点就是第一次copy的时候,tail指针的位置要落在原来的元素上,而不是copy了之后的元素上。
/* struct RandomListNode { int label; struct RandomListNode *next, *random; RandomListNode(int x) : label(x), next(NULL), random(NULL) { } }; */ class Solution { public: RandomListNode* Clone(RandomListNode* pHead) { RandomListNode* realHead=new RandomListNode(-1); realHead->next=pHead; RandomListNode* prev=realHead; RandomListNode* tail=pHead; while(tail!=NULL){ RandomListNode* newNode=new RandomListNode(tail->label); newNode->next=tail->next; newNode->random=tail->random; prev->next=newNode; prev=tail->next; tail=tail->next; } tail=pHead; RandomListNode*copyNode=NULL; while(tail!=NULL){ copyNode=tail->next; copyNode->random=copyNode->random->next; tail=tail->next->next; } prev=realHead; tail=pHead; while(tail!=NULL){ prev->next=tail->next; prev=prev->next; tail=tail->next->next; } return realHead->next; } };
判断一个单链表是否有环并找出相遇点 如何判断一个单链表中是否有环,有环的话,进入环的第一个节点,无环的话,返回空。判断单链表是否有环的操作还是挺容易的,但是找第一个节点的这个操作还是有点难,可以看之前的这个,还是死记住吧。。。关键是中间的那个方程的推导。
当然还是有简单思路的,如果没有空间复杂度的限制条件,可以采用hash表的思路进行求解,每次遍历一个元素,在hash表中将这个元素记录一下,key值可以是一个自增的index,value值可以是地址,之后如果在hash表中,发现了重复出现的元素,就说明其中有环,那么第一个重复出现的元素,也就是环开始的地方,这种方式相比于上一种,思路上是简洁明了的,实现上也是会简单许多的。
注意题目的多种解法。
稍微把之前blog中的算法做一下改动即可,前面的各种情况判断确实有点复杂,不确定重新写一次的话,还是否能够写对,前面在考虑链表长度为0和1的时候,已经考虑了许多特殊的情况。
class ChkLoop { public: int chkLoop(ListNode* head, int adjust) { ListNode *h1=head; ListNode *h2=head; bool cycle=false; int flag=0; if (head==NULL){ cycle = false; flag=1; } if (h1!=NULL&&h1->next==h1){ cycle =true; flag=1; } if (h1!=NULL&&h1->next==NULL){ cycle = false; flag=1; } while(h1!=NULL&&h2!=NULL&&flag==0){ h1=h1->next; if(h2->next!=NULL){ h2=h2->next->next; }else{ break; } if(h1==h2){ cycle=true; break; } } if(cycle==false){ return -1; }else{ h1=head; while(h1!=h2){ h1=h1->next; h2=h2->next; } } return h1->val; } };
判断两个无环单链表是否相交 考研时候的场景真是历历在目啊。
首先是遍历两个链表,得到它们的长度,m以及n,比如m>n,说明第一个链表比第二个多出m-n个节点。
之后让指向第一个链表的指针向后走m-n步,这样两个链表就在同样的起点位置了。
之后让指向两个链表的指针一起往后遍历,如果出现了公共的节点,说明两个链表在某个位置开始相交了。
当然还是有普通解法,就是类似上个题目一样,采用“万能的”hash表来实现,分别遍历两个链表,然后把其中的元素记录在hash表中,如果出现重复的,说明两个链表在这个位置开始相交了。如果一直没有出现重复的元素,说明两个链表不相交。
注意程序里面,最后一步的地方,先判断两个节点是否相同,然后再往下走,注意清楚逻辑顺序!
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/ class CheckIntersect { int getLen(ListNode* head){ int len=0; ListNode* tail=head; while(tail!=NULL){ len=len+1; tail=tail->next; } return len; } public: bool chkIntersect(ListNode* headA, ListNode* headB) { // write code here int lena=getLen(headA); int lenb=getLen(headB); ListNode*taila=headA; ListNode*tailb=headB; int temp; if(lena>lenb){ temp=lena-lenb; while(temp--){ taila=taila->next; } }else{ temp=lenb-lena; while(temp--){ tailb=tailb->next; } } //move together while(taila!=NULL&&tailb!=NULL){ if (taila==tailb){ return true; } taila=taila->next; tailb=tailb->next; } return false; } };
如何判断两个有环的单链表是否相交 如何判断两个有环的单链表是否相交,相交的话,返回第一个相交的节点,不相交的话,返回空。
当然,万能的hash表还是可以解决这个题目的。
首先要明确的一个基本概念,什么样的情况叫做链表相交,实际上更准确的表述是说,两个链表,从某个节点开始之后,剩下的部分都开始重合了,不存才那种”X”形式的相交,因为根据链表的结构,不会有两个next指针,只能是类似”Y”形的这种情况。或者是Y形后面接一个环的形状,再或者是一个环,然后分出两个小枝,两个链表在环的不同节点位置入环。
更高效的思路,首先是判断两个链表的环的起始的节点,具体方法可以根据上面的题目中的思路,比如两个如环节点分别是head1与head2
如果head1=head2,两个链表可能在入环之前就出现相交,这个时候按照上上题的方式,只不过遍历的终止节点为head1=head2
如果head1!=head2 可能是两个有换的链表根本不相交,或者如环节点是大环上的两个节点,这个时候从head1开始遍历,如果在遍历过程中找到了head2,说明是第二种情况,返回head1或者是head2都是可以的,如果在遍历的过程中,没有找到head2,又回到了head1节点,说明两个环形链表是不相交的。 没有在规定时间运行完??(需要把上一个题目的程序修改下,结束标志由NULL变为公共元素)
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; */ class ChkIntersection { int getLen(ListNode* head,ListNode*common){ int len=0; ListNode* tail=head; while(tail!=common){ len=len+1; tail=tail->next; } return len; } bool chkIntersect(ListNode* headA, ListNode* headB,ListNode*common) { // write code here int lena=getLen(headA,common); int lenb=getLen(headB,common); ListNode*taila=headA; ListNode*tailb=headB; int temp; if(lena>lenb){ temp=lena-lenb; while(temp--){ taila=taila->next; } }else{ temp=lenb-lena; while(temp--){ tailb=tailb->next; } } //move together while(taila!=common&&tailb!=common){ if (taila==tailb){ return true; } taila=taila->next; tailb=tailb->next; } return false; } ListNode* detectCycle(ListNode* head) { ListNode *h1=head; ListNode *h2=head; bool cycle=false; int flag=0; if (head==NULL){ cycle = false; flag=1; } if (h1!=NULL&&h1->next==h1){ cycle =true; flag=1; } if (h1!=NULL&&h1->next==NULL){ cycle = false; flag=1; } while(h1!=NULL&&h2!=NULL&&flag==0){ h1=h1->next; if(h2->next!=NULL){ h2=h2->next->next; }else{ break; } if(h1==h2){ cycle=true; break; } } if(cycle==false){ return NULL; }else{ h1=head; while(h1!=h2){ h1=h1->next; h2=h2->next; } } return h1; } public: bool chkInter(ListNode* head1, ListNode* head2, int adjust0, int adjust1) { // write code here ListNode * pointA=detectCycle(head1); ListNode * pointB=detectCycle(head2); if (pointA==NULL||pointB==NULL){ return false; } if(pointA==pointB){ return chkIntersect(head1,head2,pointA); }else{ ListNode*curr=pointA->next; while(curr!=pointA){ if (curr==pointB){ return true; } } return false; } } };
判断两个单链表是否相交(综合题目) 给定两个单链表的头结点head1以及head2,如何判断两个链表是否相交?相交的话返回第一个相交的节点,不想交的话返回空。
这个题目的关键之处,是不确定两个链表是否为有环链表,还是无环链表。所以只要分类讨论即可,实际上是链表相交类题目的一个综合。