141.环形链表
快慢指针slow == fast,即存在
142.环形链表2:求环的起点
slow== fast后,令slow = head,然后来个while(slow != fast)里面每个走一步,再次重合的点就是起点
a+b+c+b = 2(a+b) 即a=c
扩展问题:求环长
当两个指针首次相遇,证明链表有环的时候,让两个指针从相遇点继续循环前进,并统计前进的循环次数,直到两个指针第2次相遇。此时,统计出来的前进次数就是环长。因为指针p1每次走1步,指针p2每次走2步,两者的速度差是1步。当两个指针再次相遇时,p2比p1多走了整整1圈。因此,环长 = 每一次速度差 × 前进次数 = 前进次数。
160.相交链表:得到两个链表的交点
双指针法:显然,当两个链表长度相同时最容易找到公共节点。双指针法相当于把链表A接到链表B后面,同理又把链表B接到链表A后面,使两个链表长度相等。
设链表A的长度为a+c,链表B的长度为b+c,a为链表A不公共部分,b为链表B不公共部分,c为链表A、B的公共部分。将两个链表连起来,A->B和B->A,长度:a+c+b+c=b+c+a+c,若链表AB相交,则a+c+b与b+c+a就会抵消,它们就会在c处相遇;若不相交,则c为nullptr,则a+b=b+a,它们各自移动到尾部循环结束,即返回nullptr
思路二:链表A结束后指向B的头结点,这时就出现了环,用上面的判断环起点的方法即可。
83.删除有序链表中的重复元素:保留一个
判断当前指针的值和下一个的是否重复,是就指向下下个
82.删除有序链表中的重复元素2:都删除
遍历的时候需要记录一个prev节点,用来处理删除节点之后节点重新连接的问题,这里用到了哨兵
ListNode dummy(0);
dummy.next = head;
ListNode* p = &dummy;
21.合并两个有序链表
也需要哨兵节点。然后分别比较val大小,小的就放进去再指向下一个。最后判断哪个还有,就整个指向。
23.合并k个有序链表
采用 divide and conquer 的方法,首先两两合并,然后再将先前合并的继续两两合并。时间复杂度为O(NlgN)。
206.反转链表
207.反转[m,n]区间的链表
也得有哨兵。先指到m的位置,再在里面循环反转。
25.K个一组反转链表(困难)
遍历链表,两两交换
也有哨兵,用到前驱节点。
对链表进行排序,用分治算法,依次递归对左右两半进行排序。,最后就是合并左右两半。
旋转链表
首先需要遍历链表,得到链表长度n,因为k可能大于n,所以我们需要取余处理,然后将链表串起来形成一个环,在遍历 n - k % n 个节点,断开,就成了。譬如上面这个例子,k等于2,我们遍历到链表结尾之后,连接1,然后遍历 5 - 2 % 5 个字节,断开环,下一个节点就是新的链表头了。
两数之和
19.删除链表的倒数第N个节点
也用哨兵,双指针,当快的前进n时,慢的开始;当快的到结尾时,慢的正好到可以删除的那个节点前一个