练习题和复用代码提取
链表问题通用方法:
1.遍历结束条件:p=null || p.next=null
2.遍历的初始值:p=head
3.核心逻辑:
4.特殊情况处理(头节点,尾节点,空链表,考虑是否使用虚拟头节点)
链表中参数理解:
“=”:可以理解为指向
左侧表示被赋值的变量;右侧表示取的值
203. 移除链表元素
1.遍历结束条件:prev.next=null
2.遍历的初始条件:prev=head
3.遍历的核心逻辑:
if(prev.next.val==val)prev.next=prev.next.next;
else prev=prev.next;
4.这里可以采用虚拟头节点核心逻辑就通用;若不采用可以只对头部特殊处理判断以下
//添加虚拟头节点
ListNode newHead=new ListNode();
newHead.next=head; //图中注释1
ListNode prev=newHead; //图中注释2
while(prev.next!=null){}
return newHead.next; //图中注释3
使用快慢双指针,slow走一步,fast走两步
//注意结束条件,因为题目要求偶数个链表时,返回后面那个
//通过举例[1,2,3,4,5,6],输出返回4
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next=fast;
}
83. 删除排序链表中的重复元素
以下为链表遍历后的通用模板
ListNode newHead = new ListNode(-111, null);//创建题目值范围外的虚拟头节点
ListNode tail = newHead; //属于优化处理,在尾部加一个tail指针,方便插入
ListNode p = head; //使用p来遍历链表,防止遍历原head链表导致代码模糊
while (p != null) {
ListNode tmp = p.next; //先临时记录p.next,防止中间被修改掉,可以和p=tmp处代码结合,相当于p=p.next
if (p.val != tail.val) {
//尾部tail指针,尾部插入p链表
tail.next = p; //尾部插入三部曲
tail = p;
p.next = null;
}
p = tmp;
}
return newHead.next;
剑指 Offer 25. 合并两个排序的链表
跳过
2. 两数相加
链表尾部的null修改值
tail.next=new ListNode(carry) //表示进位为1,就将null搞为进位的1
206. 反转链表
遍历链表时维护一个向前指向的指针
while(p!=null){
ListNode tmp=p.next; //tmp指向p.next节点,并且可以记录临时的值防止p.next被修改后值变化
p.next=prev; //重点,这里维护一个指向前序的指针
prev=p;
p=tmp;
}
234. 回文链表
获取中间节点+翻转
328. 奇偶链表
跳过
25. K 个一组翻转链表
跳过
剑指 Offer 22. 链表中倒数第k个节点
快慢指针,fast先走k步
19. 删除链表的倒数第 N 个结点
跳过
160. 相交链表
遍历完一个链表继续遍历另一个,可以消除长度差
141. 环形链表
龟兔赛跑,一个跑的快一个跑的慢,在环中总归相遇