算法通关村 | 第一关 | 白银挑战篇 下

这个专题我们主要来学习一下使用双指针法来解答一些数组或链表的问题,瞧瞧它为何这么受欢迎,让我们从深层次的感受它的便捷与优秀!

算法通关村 | 第一关 | 白银挑战篇 下_第1张图片

 先来说一下什么是 双指针法吧!

何为双指针法?

我们先来带入一个场景:

想象一下,你和你的好朋友一起参加了一个寻宝活动,需要在一条长长的道路上找到宝藏。这条道路就像是一个数组或者链表,而你们俩就是双指针!

首先,你们站在道路的两端,左边是你,右边是你的朋友。你们都想尽快找到宝藏,但是不能走偏了,所以要紧紧跟着道路前进。

接下来,你们就开始行动了!你的朋友是个跑得快的家伙,他可以一次迈好几步。而你比较慢,只能一次迈一步。所以你们决定使用快慢指针的方式前进。

你的朋友作为快指针,他每次可以跳过好几个地方,快速向前移动。而你作为慢指针,只能按部就班,一步一步地走。

这样,你们俩就可以同时在道路上前进了。通过这种方式,你们能够解决一些问题,比如找到道路中的交叉点、判断道路是否有环等等。

所以,双指针就像你和你的朋友一起参加寻宝活动一样,一起在数组或链表中移动,帮助你们快速解决问题,找到宝藏!

通过这个场景,你是否对双指针有了一个初步的了解,那接下来我们就用题目实战的方式来巩固和加深一下我们对双指针的认知。

先抛一句话:双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。


先来一道简单题热热身(doge):

寻找中间节点

题目链接:876. 链表的中间结点 - 力扣(LeetCode)

题目信息:

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

算法通关村 | 第一关 | 白银挑战篇 下_第2张图片

 既然是双指针的专题,我们就先来讲一下如何使用双指针来解这道或这类题吧!

大概得思路就是,第一个快指针,一个慢指针,快的一下走两步,慢的一下走一步,当快的等于空或快的下一个为空时,慢的指向的就是中间的结点了。

 代码如下:

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head,slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

如果没有想到这种方法的话,也可以使用"笨办法",那就是遍历链表,先的到链表的长度,然后根据长度得到中间节点的位置,最后再遍历到中间结点即可。这种方法只提供思路,具体就不展开实现了,而且这种方法相比于双指针效率较低(需要两次遍历)。


寻找倒数第 K 个元素

题目链接:面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode) 

                  剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode)

题目介绍:

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

 思路:

  1. 首先,快指针 fast 先向前移动 k 步。
  2. 然后,快指针 fast 和慢指针 slow 同时向前移动,直到快指针 fast 到达链表的末尾。
  3. 此时,慢指针 slow 所指向的节点就是倒数第 k 个节点。

 具体代码如下:

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head,slow = head;
        while(fast!=null && k > 0){
            fast = fast.next;
            k--;
        }
        while(fast!=null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

注:在实际实现中,可能需要添加对输入参数 k 的合法性检查,例如判断 k 是否小于等于链表的长度。

本题也同样可以使用"笨办法",这里就不在赘述了。


旋转链表

题目链接:61. 旋转链表 - 力扣(LeetCode)

题目描述:

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置 。

算法通关村 | 第一关 | 白银挑战篇 下_第3张图片

 算法通关村 | 第一关 | 白银挑战篇 下_第4张图片

思路:

  1. 首先,根据 k 的值取模,以避免不必要的旋转。
  2. 然后,使用快慢指针的方法,快指针先向前移动 k 步,然后快指针和慢指针一起向前移动,直到快指针到达链表末尾。
  3. 此时,慢指针的下一个节点就是旋转后的新头节点,而快指针的下一个节点是原始链表的头节点。
  4. 最后,断开慢指针与快指针之间的连接,将原始链表的尾节点连接到原始链表的头节点,返回新的头节点。 

 代码如下:

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
  if (head == null || k == 0) {
            return head;
        }

        ListNode temp = head;
        ListNode fast = head;
        ListNode slow = head;
        int len = 0;
        while (head != null) {
            head = head.next;
            len++;
        }

        k = k % len;
        if (k == 0) {
            return temp;
        }

        while (k > 0) {
            k--;
            fast = fast.next;
        }

        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }

        ListNode res = slow.next;
        slow.next = null;
        fast.next = temp;
        return res;
    }
}

好了,先总结这么多,后续会持续更新好题、好解法等。

算法通关村 | 第一关 | 白银挑战篇 下_第5张图片

你可能感兴趣的:(算法通关村,算法,链表,笔记)