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

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

快慢指针

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

示例 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日

相关文章

  • JAVA多线程知识汇总

    JAVA多线程知识汇总 为什么需要多线程 在单线程模式下,当代码执行到IO操作时,CPU资源就会空闲等待IO操作完成,这样会导致CPU效率低下。而多线程模式下,线程的数量可以与CPU的核心数相匹配,能够更好地充分利用CPU资源,在IO操作等待的同时处理其他代码而不会浪费CPU。 如何使用多线程 创建线程 Java中使用继承Thread类或者实现Runnabl…

    Java 2023年5月19日
    00
  • java8日期工具类封装的实战记录

    Java8日期工具类封装的实战记录 介绍 Java8中提供的日期时间API可以更方便地处理时间日期相关的操作,提高开发效率,提高代码可读性。但是,在实际项目中,我们需要将这些API封装成工具类,方便在整个项目中使用。本文将介绍如何封装Java8日期时间API,以及如何在项目中应用。 封装Java8日期工具类 创建工具类 创建一个名为DateUtil的工具类,…

    Java 2023年5月20日
    00
  • Java构造函数的相互调用代码示例

    Java构造函数的相互调用,是指一个构造函数中调用了另一个构造函数,以达到代码复用和降低代码重复度的目的。在Java中,构造函数相互调用有两种方式:this和super。 使用this关键字调用另一个构造函数 使用this关键字调用另一个构造函数时,需要满足两个条件: this关键字必须位于构造方法中的第一行; 被调用的构造方法必须在当前构造方法之前定义。 …

    Java 2023年5月26日
    00
  • 浅谈Java响应式系统

    浅谈Java响应式系统 什么是Java响应式系统 Java响应式系统是一种基于反应式编程(Reactive Programming,简称RP)思想的编程模式,它使用异步流来构建可靠性高,性能较好的应用程序。在Java响应式系统中,数据流可以被看作是一系列的事件或者消息,应用程序可以通过订阅这些事件或者消息来进行处理。Java响应式系统常常被用于处理大规模数据…

    Java 2023年5月20日
    00
  • Java方法参数是引用调用还是值调用?

    Java方法参数是引用调用还是值调用? 在Java中,方法参数的传递方式可以分为值传递和引用传递两种方式。这是一个比较常见的问题,特别是在面试中,经常会被问到。在回答这个问题之前,我们需要对Java中的变量和内存进行一些基本的了解。 变量和内存 变量和内存是Java中的两个比较重要的概念。内存是计算机用来存储数据的地方,变量则是程序用来代表数据的符号,可以看…

    Java 2023年5月26日
    00
  • JavaSpringBoot报错“MissingServletRequestParameterException”的原因和处理方法

    原因 “MissingServletRequestParameterException” 错误通常是以下原因引起的: 缺少请求参数:如果您的请求参数缺失,则可能会出现此错误。在这种情况下,您需要检查您的请求参数并确保它们存在。 请求参数类型不正确:如果您的请求参数类型不正确,则可能会出现此错误。在这种情况下,您需要检查您的请求参数类型并确保它们正确。 解决办…

    Java 2023年5月4日
    00
  • Java线程池的分析和使用详解

    Java线程池的分析和使用详解 线程池的概念 线程池(thread pool)是线程管理的一种机制,它能够让我们更加方便地管理大量的线程,避免了频繁地创建和销毁线程,提高了程序的效率。Java中通过java.util.concurrent包提供了线程池的实现。 线程池的特点 控制线程数量 重复利用线程 管理线程 线程池的类型 Java中的线程池主要有以下4种…

    Java 2023年5月19日
    00
  • Spring Boot部署到Tomcat过程中遇到的问题汇总

    下面我将为你详细讲解“Spring Boot部署到Tomcat过程中遇到的问题汇总”的完整攻略。 一、背景知识 在部署Spring Boot应用程序的时候,通过打包为war包的方式将程序部署到Tomcat服务器上是一个常用的方式。但是在这个过程中会遇到一些问题,比如资源文件的路径问题、类加载器的问题等。 二、部署过程中应注意的问题 2.1 静态资源文件路径问…

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