链表的题目总体来说细节比较多,因为链表的题目在操作链表的过程中本身有些复杂,所以如果链表作为编程题出现的时候,多数情况下题目本身的思路可能不是很复杂,不要把题目往复杂的方向去思考就好了~这里的链表只是说单向链表,双向链表,跳表。树的链表表示形式不属于这个链表的范畴。
1.合并两个有序的链表。额外空间要求O(1)。
思路很明确的一个题目,三个指针。
2.如何判断一个链表中是否存在环?如果存在环,如何快速的找出环的起点位置。
这个题目只要见过一次就很难再忘记它的解题方法了,非常有技巧的一个方法。fast和slow两个指针,如果存在环,两个指针必定会相遇。而且相遇的时候,fast改为逐步前进,slow从头指针位置开始逐步前进,相遇的地方即为环开始的位置。这个可以利用数学知识推导一下。
3.给定一个有序的循环链表和循环链表中的一个任意指针,返回循环链表的中位数(如果是奇数,则返回中位数,如果是偶数,返回上中位数和下中位数的平均值)
有序的循环链表,可以很容易的断定链表开始的位置,最小值。这样可以统计循环链表的长度,然后从开始的位置进行计数。另外一个特殊的边界情况是所有值都相等。
4.判断两个单链表是否相交?如果两个单链表相交,求出相交的第一个节点的位置。
如果两个单链表能够行至相同的尾节点,则必定相交。
求取第一个相交节点的方法需要扫描第一个链表长度L1,第二个链表长度L2,然后将长的链表首先前进max(L1,L2) - min(L1,L2),然后两个链表的指针同时前进,第一个相遇的节点及为相交的节点。
5.如果上述的两个单链表中其中一个存在环或者两个都存在环的情况又该如何处理?
a.如果两个链表都不存在环,则使用上面的方法,
b.如果一个链表存在环,另外一个不存在,则两个链表不相遇,
c.如果两个链表均存在环,并且相遇,则说明两个链表在相遇后共享一个环。
其中这里针对每个链表,找环的方法为题目2,找环开始的位置也为题目2。在c的情况中,判断是否共享环的方法及找到其中一个链表环开始的位置,然后从该位置到该位置进行一次遍历,如果中间存在另外一个链表环开始的位置,则两个链表共享一个环,并且相遇,可以返回任意一个环开始的位置。
共享环的单链表如图:
6.实现一个函数,重新排序链表,新链表结构为偶数节点链表->奇数节点链表->NULL。举例:L0->L1->L2->L3->L4->L5->L6 => L0->L2->L4->L6->L1->L3->L5->NULL
操作当前节点的时候保存next pointer至一个临时指针,然后next = next->next,这样偶数就连偶数,奇数就连接奇数了。第一次遇到的奇数保存一个oddhead指针,eventail指针指向oddhead指针,oddtail指针指向NULL,返回evenhead指针。
7.O(1)时间内能够删除一个单链表中给定的节点吗?
针对非尾部节点,有一个小技巧可以应用一下,将给定节点的下一个节点的值copy至该节点,删除给定节点的下一个节点,看起来像是删除了该节点一样。
8.快速取得单链表倒数第K个节点。
fast,slow指针的问题,fast先走k个单位,然后fast和slow指针一起向后,fast达到尾部节点的时候,slow刚好得到倒数第k个节点。
9.单链表的逆序操作(属于单链表的基本操作,应熟练掌握)
10.判断单链表是不是回文结构?
11.针对单链表进行如下操作 L0->L1->L2->...->Ln->NULL => L0->Ln->L1->Ln-1->...Ln/2->Ln/2+1->NULL
这三个题目放在一起的原因是,10,11中将链表的逆序视为基本操作,如果不能实现链表的逆序,则10,11更无从谈起。11题目在leetcode中遇到过,所以思路很清晰,因为11题的思路能够快速的解决10,所以10的思路也很清晰。两个题目在操作之前均需要进行一个中间步骤。
完成链表后半部分的逆序:L0->L1->L2->L3->L4->L5->L6->L7 => L0->L1->L2->L3<-L4<-L5<-L6<-L7,这样就有一个head指针指向L0,一个tail指针指向L7,判断回文需要向中间靠拢即可,
需要11那种重新排序也只需要向中间靠拢即可。
寻找单链表的中位数利用寻找倒数K个节点的方法,然后+上链表逆序完成操作。
12.利用O(1)的额外空间拷贝随机链表。
随机链表指节点除了包含next还包含一个random指向链表中任意一个节点。
如果O(n)时间复杂度,建立一个新旧节点的hash表即可,如果O(1)的时间复杂度,只能采用下面的技巧了。