今日任务
- 24.两两交换链表中的节点
- 19.删除链表的倒数第N个节点
- 面试题 02.07. 链表相交
- 142.环形链表II
- 总结
LeetCode 24 两两交换链表中的节点
思路
两两交换,又要返回头节点,所以我们再次引入虚拟头节点
考虑修改一对节点的过程:第一个节点为curr
- 如果有curr且有curr.next才要修改
- 保存curr.next.next为tmp,方便下一轮修改
- curr.next.next = curr,把第二个节点指针反向
- prev.next = curr.next,让前一个节点的next指针指向第二个节点
- curr.next = tmp,把第一个节点指针指向第三个
- prev = curr; curr = tmp; 进入下一个循环
难点
如何确定每个next指针被修改的顺序?
链表里每个节点都要靠前序节点找到。所以保存一个修改一个,被用到最多的指针最后修改。 以及准备进入下一个循环时,对局部变量指针的修改顺序也很重要。
LeetCode 19 删除链表的倒数第N个节点
思路
最容易想到的方法就是遍历一次链表,得到长度l,就可以计算出节点正数是第l-n个。再从头数一次删掉它 如何在一次遍历中实现呢? 考虑双指针,如果有一个快指针fast比慢指针slow快n个节点。那么当fast到达表尾的时候,slow就处在倒数第n个节点上。
由于被删掉的也可能是头节点,为了操作的一致性,引入虚拟头节点
为了方便操作,slow指针应该指向要删除节点的前序节点,那么fast最后应该停在链表的最后一个节点,即fast.next为空 故有如下步骤:
- 把fast,slow初始化为dummyhead
- fast向前走n步
- fast和slow同步向前移动,直至fast.next为空
- slow.next = slow.next.next
- 返回dummyhead.next
LeetCode 面试题 02.07. 链表相交
思路
假设两个链表的长度差为n。考虑两个指针A和B,如果两者间距离各自的head的长度之差也为n,一起向前移动,当两个指针第一次指向同一个节点时,这个节点就是链表的交点。 故有如下步骤:
- 分别遍历两个链表得到各自长度,计算出长度差n
- 让更长的链表指针从头走n步
- 检查指针是否重合或为空
- 两个指针同向前1步,继续检查
- 如果重合,返回当前节点
- 如果某指针为空,返回null(如果
listA和listB没有交点,intersectVal为0)
LeetCode 142 环形链表2️⃣
思路
环形链表很容易想到快慢指针。考虑慢指针slow每次移动一个节点,快指针fast每次移动两个节点。两指针同时从head出发,如果两指针相遇,那一定有环;如果某指针变空,那么无环。 存在环的问题解决了,那怎么找到环的入口呢?
难点
不难看出slow和fast相遇的点一定在环上。对链表数据如上图假设,进行化简可以得到 ,再变形可以得到。
即如果两个指针同时从head和汇合点出发,每次移动一节点,当从head出发的指针走出步,到达 交点时;从汇合点出发的指针也走过步加若干个环。而不论走过多少个环,在环上位置不变。所以第二个指针也在交点。
由此可以有如下步骤:
- fast和slow指针初始化为head
- 检查fast为空或fast.next为空(链表长度有奇有偶)
- fast走两步,slow走一步
- 如果fast和slow相等,退出循环
- 如果为空,返回-1
- 如果不为空,保存当前fast/slow为meet
- 初始化另一指针node为head
- 检查head和meet是否相等
- head和meet同时向前走一步
- 相等则返回node/meet
链表总结
今日收获总结
今天学了四小时,部份双指针的题目都是有模糊的思路,却忘了怎么具体实现。实现起来也经常有细节错误,但比前两天好多了。熟能生巧~