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

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

快慢指针

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

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

相关文章

  • 详解Junit 测试之 Spring Test

    当我们用Spring框架进行开发时,经常需要对一些业务逻辑进行测试,这就需要使用到Junit进行单元测试。而Spring Test提供了一些方便的用例和注释,来使测试更加容易和完善。本篇文章将详细讲解如何使用Junit进行Spring测试。 前置条件 在进行Spring Test的开发前,需要确保以下几项内容: 已经配置了Spring框架的依赖。 已经配置了…

    Java 2023年5月20日
    00
  • 聊一聊Java反射

    聊一聊Java反射 反射是Java面向对象编程中的一种重要机制,通过反射可以在运行时获取类的信息,以及操作类的实例对象。在Java编程中,反射具有广泛的应用价值,例如通过反射动态创建对象,访问对象的私有成员变量和方法等。本文将为你详细讲解Java反射的完整攻略,包含了反射的基本使用方法、常见的场景应用以及对性能的影响等方面。 反射的基本使用方法 要使用反射,…

    Java 2023年5月19日
    00
  • Java字符串编码知识点详解介绍

    Java字符串编码知识点详解介绍 什么是字符串编码? 在计算机中,字符串是由一些字符组成的序列,而字符则是由一个或多个字节表示的。不同的字符集和不同的编码方式会影响到字符串的存储和展示。字符串编码就是将字符转换成字节的过程。 Java中的字符串编码 Java中的字符串编码默认采用Unicode编码方式,即每个字符使用两个字节表示。常见的编码方式还包括ASCI…

    Java 2023年5月20日
    00
  • Java中字节流和字符流的理解(超精简!)

    了解Java中字节流和字符流的区别和使用场景,是Java IO编程的基础。下面我们来详细讲解一下这个问题。 1. 什么是Java中的字节流和字符流? Java IO流分为字节流和字符流两种类型,它们的差别在于输入输出流所处理的数据单元不同:字节流以字节(8 bit)为单位,而字符流以字符为单位(Java中一个字符占2个字节)。 2. Java中字节流 字节流…

    Java 2023年5月27日
    00
  • hibernate 配置数据库方言的实现方法

    Hibernate配置数据库方言是一个重要的操作,因为它能让Hibernate根据不同的数据库语言,生成不同的SQL语句,从而保证操作数据库的正确性。下面是hibernate配置数据库方言的实现方法: 1.首先在Hibernate的配置文件中,需要添加一个属性:hibernate.dialect。该属性用于配置数据库方言,根据不同的数据库方言填写不同的值。例…

    Java 2023年5月20日
    00
  • Mybatis一对多查询的两种姿势(值得收藏)

    下面我来详细讲解“Mybatis一对多查询的两种姿势(值得收藏)”的完整攻略,其中包含两个示例。 概述 Mybatis作为Java开发中热门的ORM框架之一,其支持的一对多查询功能使用起来相对简单,但是需要掌握一些技巧才能发挥出它的优势。本文将介绍Mybatis中一对多查询的两种姿势,旨在帮助开发人员更好地掌握这一功能。 前置条件 在使用Mybatis一对多…

    Java 2023年5月20日
    00
  • 深入理解Java对象复制

    深入理解Java对象复制 在Java中拥有复制一个对象的需求并不少见,可是Java中的对象复制并不是一件轻松的事情。如果我们不明白Java中对象复制的本质,很容易在实现对象复制时犯错。本文将通过深入理解Java对象复制进行详细讲解。 Java中的对象复制的两种方式 在Java中实现对象复制,可以分为浅复制和深复制两种方式。浅复制只是复制了对象的引用,不会新建…

    Java 2023年5月26日
    00
  • Springboot轻量级的监控组件SpringbootAdmin

    让我来为你详细讲解一下“Springboot轻量级的监控组件SpringbootAdmin”的完整攻略。 什么是SpringbootAdmin? SpringbootAdmin是一款开源的轻量级的监控组件,它可以实时监控Spring Boot应用程序的状态、指标和环境,同时还可以提供一些管理和监控功能,比如重启应用程序、查看日志等等。 如何使用Springb…

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