前言
最近这两天在看牛客网上 链表 这块的算法题,一番苦战总结下来就是那些个套路,只要熟知一些常见的套路,碰见链表一类的算法题都会有个大概的思路。
全文包含的套路如下:
不过还有一些小细节就需要你自己在做题的过程中发现了,这里只是一些比较常见的套路,希望对你有所帮助。
快慢指针
快慢指针是指设置两个指针 slow
和 fast
,前一个指针每次移动一步,而后一个指针每次移动两步。
其中移动步数的问题是不一定的,可以根据实际情况来决定
寻找链表的中间节点
快指针每次移动两步,而慢指针每次移动一步。
ListNode fast = head;
ListNode slow = head;
while (slow.next != null && fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
ListNode mid = fast.next == null ? slow : slow.next; // 偶数 <-> 奇数
寻找倒数第 K 个节点
快指针首先移动 K 步,随后与慢指针一起到达末尾。
ListNode fast = pHead;
ListNode slow = pHead;
while (k-- < 0) {
if (fast == null) { // 长度大于链表长度
return null;
}
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
链表删除节点
删除当前节点
这个就没得说了,记录前一个节点即可。
ListNode pre = slow;
while (fast != null) {
fast = fast.next;
pre = slow;
slow = slow.next;
}
pre.next = slow.next;
删除下一个节点
ListNode cur = head;
while (cur.next != null) {
if (cur.val == cur.next.val) {
cur.next = cur.next.next; // 前后值相同只需要更换下一个节点位置即可
} else {
cur = cur.next;
}
}
反转链表
输入一个链表,反转链表后,输出新链表的表头。
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode ret = null;
if (head == null || head.next == null) {
return head;
}
ListNode pre = null; // 前一个节点
ListNode next = null; // 下一个节点
while (head != null) {
next = head.next;
head.next = pre; // 当前节点的下一个节点记为上一个节点 -> 反转
pre = head;
head = next;
}
return pre;
}
}
临时头节点
删除链表中重复出现的元素
通过设置一个 临时的头节点,这样在需要对头节点进行处理时会操作方便很多。
public ListNode deleteDuplicates (ListNode head) {
ListNode sign = new ListNode(1); // 定义临时头节点充当前一个节点
sign.next = head;
ListNode pre = sign;
ListNode cur = head;
while (cur != null && cur.next != null) {
if (cur.val == cur.next.val) {
ListNode tmp = cur.next;
while (tmp != null && cur.val == tmp.val) {
tmp = tmp.next;
}
pre.next = tmp;
cur = tmp;
} else {
pre = pre.next;
cur = cur.next;
}
}
return sign.next;
}
破坏当前节点
破坏当前节点 这种思路在判断链表是否存在环时非常的简便,同时空间复杂度 O(1)
,但是缺点就是会 破坏链表的连续性。
如果题目要求不得破坏节点时,那么也可以使用 快慢指针 来判断是否存在环。
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
if (head.next == head) { // 遇到了已经被破坏的节点,即链表中初始入环的节点
return true;
}
ListNode nextNode = head.next;
head.next = head; // 破坏节点
return hasCycle(nextNode);
}
总结
1、 记住常见的套路并作为自己的知识消化。
2、 遇题不要慌,冷静分析,可以先在纸上画出基本的解题思路,然后再下手写代码不迟。
3、 check 通过,也不要高兴,而是应该多看看别人的解题思路跟你的区别,再想想自己的思路是否还有改进的机会。
4、 学海无涯苦作舟,算法无界等你闯。
5、 这里没有说明详细的算法流程,只是作者希望你在学会套路之后可以切身体会一下算法的奥妙。
个人备注
此博客内容均为作者学习所做笔记,侵删!
若转作其他用途,请注明来源!