算法-链表中都有哪些套路

前言

最近这两天在看牛客网上 链表 这块的算法题,一番苦战总结下来就是那些个套路,只要熟知一些常见的套路,碰见链表一类的算法题都会有个大概的思路。
全文包含的套路如下:

不过还有一些小细节就需要你自己在做题的过程中发现了,这里只是一些比较常见的套路,希望对你有所帮助。


快慢指针

快慢指针是指设置两个指针 slowfast,前一个指针每次移动一步,而后一个指针每次移动两步。
其中移动步数的问题是不一定的,可以根据实际情况来决定

寻找链表的中间节点

快指针每次移动两步,而慢指针每次移动一步。

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、 这里没有说明详细的算法流程,只是作者希望你在学会套路之后可以切身体会一下算法的奥妙。


个人备注

此博客内容均为作者学习所做笔记,侵删!
若转作其他用途,请注明来源!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vgbh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值