问题1:打印两个有序链表的公共部分
【题目】 给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
解题思路:
1.准备两个指针,分别代表两个链表的头部;
2.谁小谁向前走,遇到相等的就打印,并且两个指针同时向前走一步;
3.直到任意一个指针走到空 ,停止。
问题2:判断一个链表是否为回文结构
【题目】 给定一个链表的头节点head,请判断该链表是否为回文结构。
解题思路:
方法1:
准备一个栈,将链表中的节点依次压入栈中,再依次弹出,根据栈的结构先进后出,弹出的顺序是原链表的逆序,因此每弹出一个与原链表比较,有一个不相同说明不是回文结构。
该方法用到了栈,时间复杂度O(n),额外空间复杂度为O(n),在笔试过程中可以快速答题。
注意点:
要用一个cur变量暂时存放head,因为压入栈后,还要从头开始遍历链表。
_70)方法2:
准备一个快指针,一个慢指针;快指针一次走两步,慢指针一次走一步,当快指针走完的时候,慢指针来到中点的位置;然后将慢指针后面的部分压入栈中,再与链表的前半部分对比。
该方法与方法1相比,空间节省了一半,但空间复杂度依然是O(n)。
方法3:时间复杂度O(n),空间复杂度为O(1),彻底摆脱额外空间。
准备一个快指针,一个慢指针;快指针一次走两步,慢指针一次走一步;快指针来到终点时,慢指针走到中点的位置。
从中点开始,右半部分逆序(改变指针方向),然后分别从头从尾遍历链表,一一对应,出现一个不相等,则返回false。
需要注意的是,比较完成后,需要将链表逆序部分恢复过来。(在代码过程中,尝试了不恢复原链表,不影响最后的结果判断)
问题3:将单向链表按某值划分成左边小、中间相等、右边大的形式
解题思路:
方法1:
准备一个容器,容器中放的是节点的值,从头遍历链表,将节点的值按照从小到大的顺序放容器中,再将容器中的节点连成链表返回。
时间复杂度O(N),额外空间复杂度O(N)。
主要过程:
排序子过程:
交换子过程:
方法2:
准备三个引用:small,equal,big,分别设置一个头指针,一个尾指针,分别记录小于区域,等于区域,大于区域的范围;
遍历一遍链表,分别将第一个小于pivot的位置挂在small上,第一个等于pivot的位置挂在equal上,第一个大于pivot的位置挂在big上;
然后从头重新遍历,遇到出现过的节点就跳过,然后依次将节点继续接在每个区域;
最后将每个区域的首尾相连。
时间复杂度O(N),额外空间复杂度O(1)。
问题4:反转单向链表和双向链表
时间复杂度要求为O(N),额外空间复杂度要求为O(1)
单链表解题思路:
准备两个指针,一个pre,cur,将两个指针的指向反转,并切pre,cur分别向后移动。
**双链表解题思路:**双链表每个节点都有一个上指针和下指针,分别指向该节点的上节点和下节点,在反转时,相对于单向链表,就是多了一个上节点的反转。
问题5:复制含有随机指针节点的链表
解题思路:
方法1:
利用hash表,hash表中存原始节点和复制节点的对应关系,例如1和1’,2和2’,3和3’。
通过1.next可以找到2,在hash表中又可以通过键值对找到2’,然后令2’=1’.next;通过1.random可以找到3,再通过键值对可以找到3’,令1’.random=3’。同样的方法去复制2节点和节点。
方法2:
将复制节点直接放在原节点的后面,再将后第二个原节点放在复制节点的后面,也就是1->1’->2->2’->3->3’。在此过程中,random指针不变,也就是1.random=3.
那么在复制的过程中,1’可以通过1’=1.next找到,3‘可以通过3’=3.next找到,然后将1’.next设置为3’。同理可以进行后面节点的设置。
总共分为三个步骤:
1、将原链表与复制链表连接在一起;
2、将原链表的random指针对应完成;
3、将原链表与复制链表分开,返回复制链表的头部;
小插曲:打印双向链表
先从前向后打印节点,并记录下链表结尾的位置;
然后利用last指针从后向前打印节点。
问题6:两个单链表相交的一系列问题
【题目】 在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数, 如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。 要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外空间复杂度请达到O(1)。
解题思路:
我们可以将问题一步步分解开来:
问题1:怎样判断一个链表有环无环?有环就返回环的第一个入节点,无环就返回null。
方法1:利用hashSet
依次遍历链表节点加入hashSet中,如果有环就会出现重复的节点,如果走到空都没有重复的节点,说明无环。
方法2:准备一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步;
如果链表有环,则两个指针一定会在环上相遇;
相遇时,快指针回到链表头部,两个指针分别一次走一步,直到再次相遇,此时该位置就是链表的入环点;
情况1:两个无环链表的相交
通过问题1可以判断两个链表是否有环,如果两个都无环,问题就变成两个无环链表的相交问题。
方法1:利用hashet
遍历链表1将链表1的节点放入hashSet中,再遍历链表2,如果有任何一个节点在hashSet中,则两个链表相交,此时该位置的节点就是相交节点;如果遍历链表2直到null在hashSet中都没有发现相同节点,则两个链表不相交。
方法2:
遍历链表1,得到链表1的长度以及最后一个节点;
遍历链表2,得到链表2的长度以及最后一个节点;
如果两个链表的最后一个节点不相等,说明两个链表不相交;
如果最后节点相等,说明两个链表相交。此时,让长度较长的链表先走,等到长度相等时,两个链表一起向下走,第一个相等的节点就是相交的节点。
情况2:一个链表有环另一个链表无环,此时两个链表必然不会相交。
情况3:两个有环链表的相交:
此时同样分为三种情况:
1、两个链表分别成环,此时两个链表不相交;
2、两个链表在成环之前就相交,有共同的入环节点;
3、两个链表共享一个环,入环节点不同;
对于第2种情况,寻找第一个相交节点时,可以等同于求无环链表的相交问题,只是将入环节点作为链表的终点;
对于第三种情况,遍历第一个链表,和链表2相同的第一个节点就是相交节点,如果遍历完环都没有发现有链表2相同的节点,则返回null。
最后该题的主过程如下: