Leetcode 24:
https://leetcode.com/problems/swap-nodes-in-pairs/description/
题目的第一想法:
这道题目的要求是对链表上的每对数字两两交换。那么问题在于如何在交换两个node的后把当前的两个指针移到后两个node上。这里有两点需要注意的事项:1, 如何处理边界问题;2,两个node在交换时的指针也会互换。在处理边界问题上我使用了sentinel node。使用sentinel node的好处是它可以消除前两个node的特殊性(因为需要改变head的位置)。于是我们不需要为前两个node单独设立一个case。第二个问题只要在移动指针的时候注意交换一下指针的位置就可以解决。在指针的移动上,只要确定在移动中(不是移动完)遇到null,就代表我们遇到了不可互换的node(null或者单个node),此时只要不做更改结束循环就好
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
s 1 2 3 4 5 6
s 2 1 4 3 6 5
pre current next
pre next current
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode sentinel = new ListNode(0, head);
ListNode current = head;
ListNode pre = sentinel;
ListNode next = head.next;
while(next != null){
pre.next = next;
current.next = next.next;
next.next = current;
ListNode temp = next;
next = current;
current = temp;
for(int i = 0; i < 2; i++){
next = next.next;
if(next == null){
return sentinel.next;
}
pre = pre.next;
current = current.next;
}
}
return sentinel.next;
}
}
Leetcode 19:
https://leetcode.com/problems/remove-nth-node-from-end-of-list/description/
题目的第一想法:
这道题目的解法个人感觉用递归来写较为自然。首先在‘归’的过程中可以形成一个天然的链表的倒叙状态,其次在返回值的过程中我们亦可以得到pre和current的值(current为应该被删除的node)。这道题依旧应该用到sentinel node。因为如果不使用sentinel node,当我们尝试移除链表的头部node的时候会变得十分麻烦。因此逻辑是:先递归到链表的尾部,然后再往回的过程中计算n--的值。当n--为-1时代表我们到达了pre的位置。此时进行删除的操作。然后在主体function里返回head也就是sentinel.next。最后需要注意的是在递归中如果我们想要让n的值始终如一需要一个Object来传递它,primitive type是没有保留值的功能的。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode temp = new ListNode(n);
ListNode sentinel = new ListNode(0, head);
if(head.next == null){
return null;
}
helper(sentinel, temp);
return sentinel.next;
}
public ListNode helper(ListNode head, ListNode n){
if(head == null){
return null;
}else{
ListNode next = helper(head.next, n);
if(--n.val == -1){
head.next = next.next;
next.next = null;
}
return head;
}
}
}
Leetcode 160:
https://leetcode.com/problems/intersection-of-two-linked-lists/description/
题目的第一想法:
这道题我的第一想法是依靠递归走到两层链表的最后然后在归的途中对比两个node,找出第一个不同的node返回。这个想法很难实现,因为我们很难使用同一个function来让两个长度不同的链表同时走到底部。
于是在逛了一圈讨论区后发现,这道题有两种时间复杂度相同但空间复杂度不同的写法。第一种是使用一个hashtable,在这个hashtable里面存入表A的所有node后再遍历一边表B来找第一个相同的node。这种方法很容易实行,时间复杂度是O(N+M),空间复杂度是O(N)。而第二种方法更加取巧,我们知道两个链表有重复的部分和分离的部分假设重复的部分长度为c,链表A,B分离的部分长度各为a,b。当我们设置两个指针分别指向两个链表头部时,在双方走完a+b+c个node时必然会在第一个重合点相遇。于是逻辑是我们让两个指针往前走,当走到头的时候换到另一个表的头部直到遇到相遇的点。这个方法的时间复杂度是O(N+M),空间复杂度是O(1)。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode currentA = headA;
ListNode currentB = headB;
while(currentA != currentB){
if(currentA == null){
currentA = headB;
}else{
currentA = currentA.next;
}
if(currentB == null){
currentB = headA;
}else{
currentB = currentB.next;
}
}
return currentA;
}
}
Leetcode 142:
https://leetcode.com/problems/linked-list-cycle-ii/description/
题目的第一想法:
这道题我以前做过,但不太记得证明方法了。其实如果不用hash的话在这道题里面能用的手段很有限,我能想到的唯一技巧就是快慢指针。我用快慢指针走了两遍后发现两个指针每次都会在同一个位置相遇,但没能引发更多的联想。最后还是写了hash的版本,下一次重做这道题的时候可以把快慢指针的方法做出来。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
3 2 0 4
P
P
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
ListNode current = head;
while(current != null){
if(set.contains(current)){
return current;
}
set.add(current);
current = current.next;
}
return null;
}
}