24. 两两交换链表中的节点
记录之前的代码:
class Solution {
public ListNode swapPairs(ListNode head) {
if (head==null) return head;
ListNode fakeNode = new ListNode();
fakeNode.next = head;
ListNode left = fakeNode, mid=head, right=head.next;
while(right!=null){
mid.next = right.next;
right.next = mid;
left.next = right;
mid = mid.next;
if (mid == null){ break; }
left = right.next;
right = mid.next;
}
return fakeNode.next;
}
}
第一次尝试:未注意到,除了两两之间需要转换,其实每一对之间也要转换next指针。
不要忘记虚拟头节点啊!!
class Solution {
public ListNode swapPairs(ListNode head) {
// 边缘条件
if (head==null || head.next==null) return head;
//
ListNode pre = head, cur = head.next, temp, resHead = head.next;
while(cur!=null){
// swap
temp = cur.next;
cur.next = pre;
pre.next = temp;
// 循环条件更新
pre = temp;
if(pre==null){
break;
}else{
cur = pre.next;
}
}
return resHead;
}
}
修正:
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0, head);
// 边缘条件
if (head==null || head.next==null) return head;
//
ListNode pre = dummy, cur = head, next = head.next, temp;
while(next!=null){
// swap
temp = next.next;
next.next = cur;
cur.next = temp;
// swap之后 循环条件更新
pre.next = next; // 重点:每组之间
pre = cur;
cur = temp;
if(cur==null){
break;
}else{
next = cur.next;
}
}
return dummy.next;
}
}
随想录中还有递归解法,同样尝试实现了一个:
class Solution {
public ListNode swapPairs(ListNode head) {
// 终止条件
if(head==null || head.next==null) return head; // 返回值:返回的是两组之中,交换之后的,第一个节点
// 递归逻辑
ListNode node1 = head.next;
ListNode temp = head.next.next;
node1.next = head;
head.next = swapPairs(temp);
return node1;
}
}
总结
查看代码随想录版本,并总结。
- 命名:面对众多指针的情况,尽量使用cur, node1, node2 。明确cur的位置,有助于进行循环。
- 交换顺序:如果更改交换顺序,则可以不用temp节点。
- 递归解法:可以先交换……虽然还是要临时节点来存储。
// 将步骤 2,3 交换顺序,这样不用定义 temp 节点
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0, head);
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null) {
ListNode node1 = cur.next;// 第 1 个节点
ListNode node2 = cur.next.next;// 第 2 个节点
cur.next = node2; // 步骤 1
node1.next = node2.next;// 步骤 3
node2.next = node1;// 步骤 2
cur = cur.next.next;
}
return dummy.next;
}
// 递归版本
class Solution {
public ListNode swapPairs(ListNode head) {
// base case 退出提交
if(head == null || head.next == null) return head;
// 获取当前节点的下一个节点
ListNode next = head.next;
// 进行递归
ListNode newNode = swapPairs(next.next);
// 这里进行交换
next.next = head;
head.next = newNode;
return next;
}
}
19.删除链表的倒数第N个节点
双指针法:需要想清楚
- 删除倒数第n个,则指针指向倒数第n+1个。
- 遍历到链表尾的条件:二选一 —— 指向null 、指向最后一个节点。
- 很容易推导出:right与left之间相距n。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
// right指向倒数第一个,left指向倒数第n+1个。相差n。
ListNode left = dummy, right = dummy;
// 先将right遍历。 循环结束时,相差n。
for(int i=0; i<n; i++){
if(right.next == null) return dummy.next;
right = right.next;
}
while(right.next!=null){
right = right.next;
left = left.next;
}
left.next = left.next.next;
return dummy.next;
}
}
面试题 02.07. 链表相交
注意:交点不是数值相等,而是指针相等。
想试试合并链表同步移动法。但是超时:
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
while(curA!=curB){
curA = curA.next;
curB = curB.next;
if (curA==null) curA = headB;
if (curB==null) curB = headA;
}
return curA;
}
}
与代码随想录对比:
(版本二) 合并链表实现同步移动
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// p1 指向 A 链表头结点,p2 指向 B 链表头结点
ListNode p1 = headA, p2 = headB;
while (p1 != p2) {
// p1 走一步,如果走到 A 链表末尾,转到 B 链表
if (p1 == null) p1 = headB;
else p1 = p1.next;
// p2 走一步,如果走到 B 链表末尾,转到 A 链表
if (p2 == null) p2 = headA;
else p2 = p2.next;
}
return p1;
}
}
主要问题在于:while的判断条件,并未处理curA=null
,或curB=null
时的情况。
不应该跳过null
:如果两者同时等于null
,只会有两种情况。都能够保证不会无限循环。
- 两链表长度相同,并且没有交点。(在组合链表之前)
- 两链表长度不同,并且没有交点。(在组合链表之后)
即,组合链表时,两链表之间应该插入一个null,而不是单纯连在一起。
对于正常解法,应该是算出两者长度之差,然后在倒数第n个节点(假设是n),同时开始遍历。
142.环形链表II
这道题算是很难的一道题(对我来说),因为需要额外的数学推导。
/*
两者最终相交时,进行了c次循环
fast:走了2c
slow:走了1c
长度 x, y, z
已知:
x + y + n(y+z) = 2c
x + y = 1c
得出:
n(y+z) = x+y
得出:
x = n (y + z) - y 化为 x = (n - 1) (y + z) + z
求x:
n取1,2,3……不同值时,总有 ——
在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。
让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
n的含义:index1在环里一共转了n圈。
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
while(true){
if (fast==null || fast.next==null) return null;
fast = fast.next.next;
slow = slow.next;
if(fast==slow) break;
}
ListNode node1 = head, node2 = fast;
while(node1!=node2){
node1 = node1.next;
node2 = node2.next;
}
return node1;
}
}