面试题快慢链表和快慢指针

快慢链表和快慢指针是算法中常见的一种技巧。它们在链表中查找中间节点、判断链表是否有环等情况下十分实用。下面就对快慢链表和快慢指针的使用进行详细讲解。

快慢指针

快慢指针的基本思想是将两个指针指向链表的头节点,快指针每次走两步,慢指针每次走一步,当快指针走到链表的末尾时,慢指针指向的就是链表的中间节点。

示例 1: 找到链表的中间节点

我们有一个链表,包含以下元素:1→2→3→4→5。

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
head.next.next.next = new ListNode(4);
head.next.next.next.next = new ListNode(5);

为了找到链表的中间节点,我们使用快慢指针的方式。

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

上面的代码中,我们先将快慢指针都指向链表的头节点,然后移动指针。快指针每次向后移动两个节点,慢指针每次向后移动一个节点,当快指针到链表的末尾时,慢指针所指向的就是链表的中间节点。

示例 2: 判断链表是否为回文链表

问题描述:给定一个单链表,判断它是否为回文链表。

解决方案:使用快慢指针找到链表的中点,然后将链表的后半部分反转。最后将前半部分和后半部分进行对比,如果相同,则说明该链表是回文的。

public boolean isPalindrome(ListNode head) {
    if (head == null) return true;
    ListNode middleNode = middleNode(head);
    ListNode secondHalf = reverse(middleNode.next);
    ListNode p1 = head, p2 = secondHalf;
    boolean result = true;
    while (result && p2 != null) {
        if (p1.val != p2.val) {
            result = false;
        }
        p1 = p1.next;
        p2 = p2.next;
    }
     // 将链表还原
    middleNode.next = reverse(secondHalf);
    return result;
}

// 反转后半部分链表
public ListNode reverse(ListNode head) {
    ListNode prev = null, curr = head;
    while (curr != null) {
        ListNode tmp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = tmp;
    }
    return prev;
}

在上面的代码中,我们使用快慢指针寻找链表的中点,并将中点后面的链表反转,然后将前半部分和后半部分分别赋值给p1和p2,进行对比即可。

快慢链表

快慢链表的基本思想是将链表的指针指向头节点,同时让两个指针同时向前移动。快指针每次移动两个节点,慢指针每次移动一个节点。当快指针到达链表末尾时,慢指针到达链表中点。

示例 1: 判断链表是否有环

问题描述:给定一个链表,判断该链表是否有环。

解决方法:使用快慢链表,如果链表中有环,则快指针最终一定会追上慢指针,如果没有,则快指针会走到链表的末尾。

public boolean hasCycle(ListNode head) {
    ListNode fast = head, slow = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow == fast) {
            return true;
        }
    }
    return false;
}

上面的代码中,我们使用快慢链表遍历链表,如果链表中有环,则快指针最终会追上慢指针,如果没有环,则快指针到达链表的末尾时退出循环。

示例 2: 寻找环的起点

问题描述:给定一个链表,如果链表中有环,找到环的起点。

解决方法:使用快慢链表,如果指针相遇,说明链表中有环。然后将两个指针移动到链表的头节点处,然后同时移动两个指针,每次移动一个节点,当两个指针再次相遇时,该节点即为环的起点。为什么这么做是正确的呢?因为当两个指针相遇时,假设慢指针所走的距离为S1,环的周长为C,相遇时慢指针走了n个环,那么此时快指针走了2n个环,快指针从开始到相遇所走的路程为S1 + nC,而快指针从开始到相遇所走的路程为 2(S1 + nC)。因为快指针所走的路程是慢指针的两倍,因此我们可以推出 S1 = (n-1)C + (C-S2),其中S2是慢指针所走的距离。根据上面的公式,我们将快指针再移动n-1个环,此时快指针到达环的起点处,同时,我们让慢指针从头节点开始移动,当两个指针相遇时,即为环的起点。

public ListNode detectCycle(ListNode head) {
    ListNode fast = head, slow = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow == fast) {
            break;
        }
    }
    if (fast == null || fast.next == null) {
        return null;
    }
    // 找到环的起点
    slow = head;
    while (slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
}

上面的代码中,我们先使用快慢链表判断链表是否有环,如果链表中有环,则快慢指针会相遇。接下来,我们将快指针指向头节点,然后将快慢指针同时向前移动,当两个指针再次相遇时,该节点即为环的起点。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:面试题快慢链表和快慢指针 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • IDEA全局查找关键字的用法解读

    下面就为大家详细讲解“IDEA全局查找关键字的用法解读”的完整攻略。 1. 什么是IDEA全局查找 IDEA全局查找是指在IDEA中查找某个关键字时,不仅可以在当前文件中查找,还可以在整个项目中查找。 2. 如何使用IDEA全局查找 使用IDEA全局查找非常简单,具体步骤如下: 打开需要查找的项目。 在菜单栏中点击“Edit” -> “Find” -&…

    Java 2023年6月15日
    00
  • jsp实现剪子石头布小游戏

    实现一个剪子石头布小游戏的完整攻略需要以下几个步骤: 创建一个JSP网页,用于显示游戏界面,用户可以进行游戏选择和游戏操作。 在JSP网页中使用HTML和CSS,创建游戏界面。可以使用Canvas或HTML DOM创建游戏操作界面。 在JSP网页中,使用JavaScript编写游戏逻辑。游戏逻辑包括用户输入判断、计算得分、更新游戏状态等。 将JSP网页和游戏…

    Java 2023年6月15日
    00
  • 详解微信小程序实现仿微信聊天界面(各种细节处理)

    详解微信小程序实现仿微信聊天界面(各种细节处理) 1. 概述 本攻略旨在利用微信小程序的基础组件和API实现仿微信聊天界面的功能。其中包括对聊天记录的渲染、语音和图片消息的处理、滚动条的实现、输入框的处理以及底部工具栏的实现等。 2. 渲染聊天记录 在小程序中,我们可以使用wx:for将聊天记录数组渲染到页面中。为了使聊天界面更加真实,我们需要对每一条聊天记…

    Java 2023年5月23日
    00
  • java基于spring注解AOP的异常处理的方法

    我来分步骤讲解Java基于Spring注解AOP的异常处理的方法: 步骤一:创建异常处理器 首先需要创建一个异常处理器,用于捕获和处理程序中可能会遇到的异常。 package com.example.demo.exception; import org.springframework.web.bind.annotation.ControllerAdvice;…

    Java 2023年5月27日
    00
  • java关于string最常出现的面试题整理

    让我来就这个话题给你提供一些完整的攻略。 1. String常见的面试题目 在Java的面试中,String类往往是必考的题目,下面列出几个比较常见的问题: String类是不可变的,你是怎么理解的? String类的equals()和==的区别是什么? String类中常用的方法有哪些? StringBuffer和StringBuilder有什么区别? 2…

    Java 2023年5月27日
    00
  • springboot打包如何忽略Test单元测试

    使用Maven插件 首先在pom.xml中使用Maven插件,添加如下代码段,其中,true表示不执行单元测试: <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spri…

    Java 2023年5月19日
    00
  • 什么是线程池?

    以下是关于线程池的完整使用攻略: 什么是线程池? 线程池是一种用于管理和复用线程的机制,它可以在程序启动时创建一定数量的线程,并将这些线程保存在一个池中,当需要执行任务时,从池中取出一个线程执行任务,任务执行完成后,线程不会被销毁而是返回到池中等待下一次任务的执行。线程池可以有效地减线程的创建和销毁次数,从而提高程序的性能和效率。 线程池的优点 线程池的优点…

    Java 2023年5月12日
    00
  • Java如何将处理完异常之后的程序能够从抛出异常的地点向下执行?

    在 Java 中,可以使用异常处理机制来捕获和处理异常,如果在程序执行过程中抛出了异常,那么程序会立即停止执行。为了解决这个问题,Java 提供了异常处理机制,可以通过捕获异常并处理它们,使得程序能够继续执行下去。 当程序运行时发生异常时,程序会跳转到与异常相符的 catch 语句块,并执行该语句块中的代码,处理完异常后可以通过尝试继续执行下一个块语句来使程…

    Java 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部