前言
本文是第二篇跟Leetcode算法相关的文章,题目顺序是根据代码随想录刷的 代码随想录
其他文章链接:刷算法Leetcode文章汇总
链表篇
①设置空头节点,从头遍历链表
②不设置空头结点,先对头节点判空,再看next
自己设计ListNode类
记得设置字段size,链表长度,用于判断index是否越界,注意add时size++,delete时size--
①单向链表,不使用空头结点,每次要判断head是否为null或者size是否为0
②单向链表,使用空头节点,直接在空头节点后操作即可
③双向链表,头尾节点不空,条件判断复杂,没成功实现
④双向链表,头尾节点为空节点,初始化时将头尾节点链接,add时改变指向用一行代码即可,如
temp.pre.next = temp.pre = new Node(val,temp.pre,temp);
①迭代,三指针,前两个指针用于改向,最后一个用于保留后边的链表
②递归,创建成员变量记录最深递归的节点为新头节点,每次return head,即新链表的尾节点,逐步回溯将指针改向
class Solution {
ListNode newHead;
public ListNode reverseList(ListNode head) {
reverse(head);
return newHead;
}
public ListNode reverse(ListNode node){
if(node == null || node.next == null){
newHead = node;
return node;
}
reverse(node.next).next = node;
node.next = null;
return node;
}
}
③递归,每次return newHead,即新链表的头节点,获得头之后,再将当前head.next的指针改向
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
①迭代,使用空头节点记录新头。三指针,第一个记录已完成链表的末尾,后两个分别为待交换链表的前两个。注意改向顺序,先将第一个指向第三个,再将第二个指向第三个的next,再将第三个指向第二个,最后更新第一个指针
②递归,类似于反转整个链表,先保存新头节点newHead,再更新head.next,将head.next.next用于递归,再更新newHead.next
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null) return head;
ListNode newHead = head.next;
head.next = swapPairs(head.next.next);
newHead.next = head;
return newHead;
}
①第一遍记录长度,第二遍找到要删除的位置
②快慢指针,快指针先走n+1步(根据快慢指针的具体设计改变)
③栈,先将节点全入栈,再将栈顶k个出栈,修改倒数k+1个节点的指向
①快慢指针,记录两个链表的长度,快指针先走长度差的步数
②Set,先将headA中节点加入集合,再找headB中第一个与Set中相同的节点
③双指针,利用相交链表的特征,第二次遍历都从另一个头节点开始,长度:a+b = b+a,利用三目运算符简化代码
while(tempA != tempB){
tempA = tempA == null ? headB : tempA.next;
tempB = tempB == null ? headA : tempB.next;
}
①Set,从头结点开始放入Set中,第一个重复的就是入口节点
②快慢指针,慢指针走一步快指针走两步,最终在环内某一点相交,然后分别从相交节点和头节点开始往后,下一次相交的位置就是入口节点,理论推导:
头节点到环入口a,环入口到相遇点b,相遇点回到环入口c,假设快指针一共走了环n圈
慢指针a+b,快指针a+n(b+c)+b,即2(a+b)=a+n(b+c)+b,得a=(n-1)b+nc=c+(n-1)(b+c),即头节点到环入口a等于相遇点到环入口c加上n-1圈长,因此分别从头节点和相遇点开始移动一定会在入口点相遇
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null)return null;
ListNode temp1 = head.next, temp2 = head.next.next;
while(temp1 != null && temp2 != null && temp2.next != null){
if(temp1 == temp2)break;
temp1 = temp1.next;
temp2 = temp2.next.next;
}
temp2 = head;
while(temp1 != null){
if(temp1 == temp2)return temp1;
temp1 = temp1.next;
temp2 = temp2.next;
}
return null;
}
}
链表总结
1、链表的操作要注意边界条件,注意每个题head、next或者next.next为空的情况
2、通常使用空头节点会大大便利链表头节点为空的情况
3、遇到前后个数、相交链表、环形链表等,用快慢指针会很方便
4、对于倒数问题,可以利用栈先进先出的特性解决
5、对于重复性问题,如相交链表和环形链表,可以使用Set进行判断