24.两两交换链表中的节点
看完题后的思路
用两个指针pre,q指向1,2,创建一个虚拟头结点,使用尾插法插入**.难点在于初始条件的两个指针判空与终止条件的判断(奇数个节点与偶数个节点)**
初始节点判空无非三种情况,空节点,一个节点,直接使用一个判断解决
当个数是奇数时,pre指向最后一个元素,q指向空.当是偶数是,pre与q皆为空.
可以只考虑偶数情况,终止条件为两个都不为空.奇数情况菱形判断.
思路
见上面
代码
//24.两两交换链表中的节点
public ListNode swapPairs(ListNode head) {
// 初始条件判断
if(head==null||head.next==null){
return head;
}
ListNode ihead=new ListNode(-1);
ListNode pre=head,q=head.next;
ListNode tail=ihead;
//核心代码
while (pre!=null&&q!=null){ //通过分析结尾情况,值考虑偶数情况
tail.next=q;
tail=q;
q=q.next;
tail.next=pre;
tail=pre;
pre=q;
if (q!=null){ // 双指针执行中间的赋值情况
q=q.next;
}
}
if(pre!=null){ //考虑奇数情况
tail.next=pre;
tail=pre;
}
tail.next=null; // 尾插法最后一步,防止成环
return ihead.next;
}
复杂度
时间复杂度 0(n)
空间复杂度0(1)
看一遍代码就行,三刷可以直接过
困难/进一步优化/收获
收获
1. 本题解决了 链表中相邻双指针的初始情况判断 执行中赋值问题 循环结尾问题
2. 代码随想录上的next next.next感觉不如我的版本简洁
19.删除链表的倒数第N个节点
看完题后的思路
删除链表中的第导数n个节点,就要找到链表中的倒数第n+1个节点.如果链表长度为l,x为第导数n+1个节点的index,x+1+n=l,x=l-n-1.
创建虚拟头结点可以更方便的删除头结点.
思路
见上文
代码
//19.删除链表的倒数第N个节点
public ListNode removeNthFromEnd(ListNode head, int n) {
int size=0;
ListNode ihead=new ListNode(-1,head); // 方便输出第一个节点
ListNode curr=head;
while (curr!=null){
size++;
curr=curr.next;
}
if (size<n){
return null;
}
int index=size-n-1; // 第一个节点,前一个index为-1
if (index==-1){
ihead.next=ihead.next.next;//size<n保证ihead.next不为空
return ihead.next;
}
//当不是删除第一个节点时
curr=ihead.next;
while (index>0){
curr=curr.next;
index--;
}
curr.next=curr.next.next;
return ihead.next;
}
复杂度
时间复杂度 0(n)
空间复杂度 0(1)
困难/进一步优化/收获
收获 快慢指针法
代码随想录上提供的方案更好,使用双指针,质询要遍历链表一步就行
fast先走n步([slow,fast]之间有n+1个节点),当fast走向末尾,slow刚好在倒数第n+1个节点上.
//快慢指针法
public ListNode removeNthFromEnd02(ListNode head, int n) {
ListNode ihead=new ListNode(-1,head); // 方便输出第一个节点
ListNode fast=ihead,slow=ihead;
while (n>0){
if (fast==null){
return null;// 判断当n大于链表长度
}
fast=fast.next;
n--;
}
while (fast.next!=null){
fast=fast.next;
slow=slow.next;
}
slow.next=slow.next.next;
return ihead.next;
}
看一遍代码就行,三刷可以直接过
链表相交
看完题后的思路
想到了用两个指针,但是这两个指针之间的关系没有想出来
思路
a+c+b=b+c+a
代码
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//初始判断
if (headA==null||headB==null){
return null;
}
ListNode aCurr=headA,bCurr=headB;
int nullNum=2;
while (aCurr!=bCurr){
if (aCurr.next==null){
if (nullNum>0){
nullNum--;
aCurr=headB;
}else {// 第三次为空,一定不相交
return null;
}
}else {
aCurr=aCurr.next;
}
if (bCurr.next==null){
if (nullNum>0){
nullNum--;
bCurr=headA;
}else {
return null;
}
}else {
bCurr=bCurr.next;
}
}
return aCurr;
}
复杂度
时间复杂度 0(n)
空间复杂度 0(1)
困难/进一步优化/收获
做链表题,第一步要先考虑初始条件,例如链表为空什么的.本题一开始未考虑空链表情况,代码不通过.
三刷可以写一遍代码
环形链表II
思路
代码
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null){
return null;
}
ListNode slow=head,fast=head;
int bootstrap=1; //启动
while (bootstrap>0||slow!=fast){
if(bootstrap==1){
bootstrap--;
}
slow=slow.next;
if (fast==null||fast.next==null){
return null;
}
fast=fast.next.next; // 因为有环,所以一定不会NPE
}
// 找到相遇点
ListNode a=head;
ListNode b=slow;
while (a!=b){ // 一定会相遇
a=a.next;
b=b.next;
}
return a;
}
}
复杂度
困难/进一步优化/收获
收获 为什么下面的循环条件会出错
while (slow==head||slow!=fast){
System.out.println(slow.val);
slow=slow.next;
if (fast==null||fast.next==null){
return null;
}
fast=fast.next.next; // 因为有环,所以一定不会NPE
}
可以模拟一下,上面的情形确实会陷入死循环
改进:
ListNode slow=head,fast=head;
int bootstrap=1; //启动
while (bootstrap>0||slow!=fast){
if(bootstrap==1){
bootstrap--;
}
slow=slow.next;
if (fast==null||fast.next==null){
return null;
}
fast=fast.next.next; // 因为有环,所以一定不会NPE
}