刷题-链表-API及套路总结
一、链表-简单-API及套路总结
1、链表-简单-约7题
1)206.反转链表
【题目特点】:
【原理】: 移动双指针/递归
迭代:使pre=null,cur=head,每次循环,申请临时节点存储当前节点的下一位,即,tmp=cur.next。
然后将当前节点的下一位指向pre,即cur.next = pre,断开连接。当前节点赋值上一节点,即pre=cur。
同时,临时节点赋值给当前节点,cur=tmp,这样两个指针完成一步移动。循环往返,当cur为null,此时pre指向尾部即头节点。
递归:先不断递归到尾部,然后返回尾结点。
同时head.next.next = head,将尾部指向上一个节点,head.next = null,上一个节点的下一个置为null。
最后每次返回尾结点,即头节点(倒转后)。
【套路】:
ListNode tmp = cur.next;
cur.next = pre ;
pre = cur ;
cur = tmp;
【复杂度】:时间复杂度为O(n),空间复杂度为O(1).
ListNode tmp = cur.next;//申请临时存储节点
cur.next = pre;//将下一位反向指向第一位
pre = cur;//移动第一位,从而第三位指向第二位
cur = tmp;//移动当前第二位往前移动一位,从而真实第二位反指向第一位实现反转
2)21. 合并两个有序链表
【题目特点】:
【原理】: 递归或双指针。与合并两个数组原理类似,类似双指针法,将较小的数值移动到临时存储空间内
【复杂度】:时间复杂度为O(n+m),空间复杂度为O(1).
写法套路
ListNode head = new ListNode(-1); 创建空头节点
ListNode tmp = head;//将头节点赋予临时节点,同时临时节点作为移动指针不断移动。
while(l1!=null&&l2!=null){}
tmp.next=l1==null?l2:l1;// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
3)141. 环形链表
【题目特点】:
【原理】: 1、定义快慢指针,定理,若是环形链表,则快慢指针会相遇。
2、哈希表,遍历并添加到set集合中,若有重复则返回true,若最终为null,则不是环。
【复杂度】:时间复杂度为O(n),空间复杂度为O(1).
4)剑指 Offer 22. 链表中倒数第k个节点
【题目特点】:
【原理】: 第一想法是先遍历整个链表求出长度,最后遍历n-k。不推荐
可以利用双指针的思想,1、先让快指针前进k步。
2、快慢指针同时前进n-k步(即快指针的移动到尾端,此时慢指针移动了n-k),
此时慢指针所在位置为倒数第k个节点。
【复杂度】:时间复杂度为O(n),空间复杂度为O(1).
5)剑指 Offer 06. 从尾到头打印链表
【题目特点】:
【原理】: 利用栈的思想,遍历链表压栈,最后再弹栈,同时存储到数组中返回。
【复杂度】:时间复杂度为O(n),空间复杂度为O(1).
Stack<ListNode> stack = new Stack<ListNode>();
ListNode temp = head;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] print = new int[size];
for (int i = 0; i < size; i++) {
print[i] = stack.pop().val;
}
return print;
6)234. 回文链表
【题目特点】:
【原理】: 思路一:复制链表值到数组列表中,使用双指针法判断是否为回文。
思路二:递归
思路三:快慢指针。找到中间位置,偶数为右边位置,奇数为中间,然后传参将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。比较完成后将链表再反转回来。
【复杂度】:时间复杂度为O(n),空间复杂度为O(1).
套路:快慢指针可以找到链表的中间。
if (head == null) {
return true;
}
// 找到前半部分链表的尾节点并反转后半部分链表
ListNode firstHalfEnd = endOfFirstHalf(head);
ListNode secondHalfStart = reverseList(firstHalfEnd.next);
// 判断是否回文
ListNode p1 = head;
ListNode p2 = secondHalfStart;
boolean result = true;
while (result && p2 != null) {
if (p1.val != p2.val) {
result = false;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表并返回结果
firstHalfEnd.next = reverseList(secondHalfStart);
return result;
}
private ListNode endOfFirstHalf(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
2、刷题总结
基本上最常用的为快慢指针即双指针来解题,其次为递归,但是一般递归可能现场有点反应不过来,
所以可以先采用双指针来解题。若遇到【引用重复】的含义即相同的节点、交点可以采用哈希表set集合来解。
从尾部遍历可想到栈的思想Stack.
快慢指针:【中间】
环形链表:可以理解成两个人速度一致, 走过的路程一致。那么肯定会同一个时间点到达终点。
二、链表-中等-API及套路总结
1、链表-中等-约12题
1) 2. 两数相加
【题目特点】: 移动指针+数学; 并进行相加,累计进位
【复杂度】:O(max(m,n)) , O(1)
【套路写法】:
//创建虚拟头节点,并新建一个复制一份方便移动。
ListNode head = new ListNode(-1);
ListNode cur = head;
//三目运算
int n1 = l1!=null?l1.val:0;
//余数与商
cur.next = new ListNode(sum % 10);
carry = sum / 10;
【总结】:掌握余数和除数,掌握三目运算。掌握创造虚拟。
2)19. 删除链表的倒数第 N 个结点
【题目特点】:属于快慢指针
【感想】:与简单题中返回倒数第n个节点类似,不过多了一步。
【复杂度】:O(n),O(1)
【套路写法】:
ListNode dummy = new ListNode(0, head);和下面二者一个道理
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
【总结】:掌握创造虚拟节点
掌握 [简单题中 返回倒数第n个节点],即:快指针从n开始走,走N-n,从而慢指针走了N-n,就走到倒数n个节点。
掌握 删除下一个节点:second.next = second.next.next;
3)24. 两两交换链表中的节点
【题目特点】: 属于交换链表
【解题方法】:递归或是迭代,迭代的大致思路是创建三个节点,一个主节点,两个临时节点,通过来回交换,从而移动两个节点的位置。注意是位置而不是节点的值
【复杂度】:O(n),O(1)
【套路写法】:
//创建虚拟节点,或是头节点,令其下一位指向头节点,这样做的好处是移动的时候可以,刚好移动目标点位之前,从而方便做一些操作。最后结束返回dummyHead.next;完美
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
return dummyHead.next;
// 交换两个节点的位置的套路
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
tmp = node1;
【总结】:掌握创造虚拟节点。
掌握交换两个节点的写法。
4)92. 反转链表 II
【总结】:该题与简单题类似,是简单题的升级版,简单题的反转是将整个链表反转,而该题是将中间一部分进行反转。
但是该题是头插,移动并往头部插入节点,和简单题稍微有点不太一样。
next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
5)61. 旋转链表
【题目特点】:移动链表,和两数相加都用到取余操作
【原理】:
【套路写法】:
int add = n - k % n;//计算移动k个元素,移动前面的个数,计算从哪个断开
6)142. 环形链表 II
7)82. 删除排序链表中的重复元素 II
【题目特点】:
【原理】:核心点是创造哑节点,并比较下一个和下下个,一样的话赋值给x,并循环比较同时删除
【套路写法】:
8)146. LRU 缓存机制
9)148. 排序链表
10)86. 分隔链表
【题目特点】:
【原理】:核心点是创造小链表和大链表,将小于该数存到小链表里,将大于等于改数存到大链表里。最后将大链表置空,将小链表的next指向大链表头部。
【套路写法】:
2、套路总结
感觉中等题目,比简单题目,多了一步操作。原理还是简单题的组合。
很多题都创造哑节点,比较下一位和下下位
模板
【题目特点】:
【解题API】:
【解题核心】:
【原理】:
【提升解析】:
【拓展点】:
【复杂度】:
【套路写法】: