链表面试题之判断是否为回文链表—三种方式解决,总有一种方法让面试官满意

简介: 链表面试题之判断是否为回文链表—三种方式解决,总有一种方法让面试官满意
  座右铭:代码虐我千百遍,我待代码如初恋!!!

给定一个单链表的头节点head,请判断该链表是否为回文结构。

   栈方法特别简单(笔试用)

   改原链表的方法就需要注意边界了(面试用)

第一种方法:用一个栈来辅助实现,将链表从头结点开始依次压入栈中,之后再从栈顶依次弹出并与原链表一个一个进行比较,,如果相比较的过程全部相同,说明是回文结构,如果有一个比对不上就不是回文结构。因为栈的特征是先进后出的,所以弹出的过程就是逆序的过程。这个方法比较简单,缺点就是费点空间,需要O(N)的空间复杂度

第二中方法:第一种基础上改进一下,同样需要一个栈来辅助实现,但是只需要用到栈的一半空间,所以空间复杂度为O(N/2)。额外申请两个变量,一个快指针和慢指针。让慢指针指向链表右半部分的第一个结点,然后将右半部分压入栈中,再从栈中弹出和左半部分一一比较

第三中方法:不用申请栈,只需要O(1)的空间复杂度。申请一个快指针和慢指针,链表结点个数为奇数的时候慢指针来到中点位置,;偶数个的时候,慢指针来到上中点位置。然后将右半部分链表反转,慢指针指向的结点指向空。此时再让慢指针和快指针分别从最右边和最左边开始一一进行比较。最后返回结果之前一定要将右半部分链表反转回来!!!此方法难点在于难抠边界,但是是最优解,适合面试的时候和面试官聊聊哈哈哈~~~///(v)\\\~~~

package com.harrison.class06;
import java.util.Stack;
public class Code02_IsPalindromeList {
  public static class Node {
    public int value;
    public Node next;
    public Node(int v) {
      value = v;
    }
  }
  // need n extra space
  public static boolean isPalindrome1(Node head) {
    Stack<Node> stack = new Stack<Node>();
    Node cur = head;
    // 将链表从头结点开始依次压入栈中
    while (cur != null) {
      stack.push(cur);
      cur = cur.next;
    }
    // 从栈顶依次弹出和链表开始比较
    while (head != null) {
      if (head.value != stack.pop().value) {
        return false;
      }
      head = head.next;
    }
    return true;
  }
  // need n/2 extra space
  public static boolean isPalindrome2(Node head) {
    if (head == null || head.next == null) {
      return true;
    }
    Node right = head.next;// 慢指针
    Node cur = head;// 快指针
    while (cur.next != null && cur.next.next != null) {
      right = right.next;
      cur = cur.next.next;
    }
    // right 奇数个来到中点位置 偶数个来到下中点位置
    Stack<Node> stack = new Stack<>();
    // 链表的右半部分依次压入栈中
    while (right != null) {
      stack.push(right);
      right = right.next;
    }
    // 从栈中依次弹出 和链表左部分依次比较
    while (!stack.isEmpty()) {
      if (head.value != stack.pop().value) {
        return false;
      }
      head = head.next;
    }
    return true;
  }
  // need O(1) extra space
  public static boolean isPalindrome3(Node head) {
    if (head == null || head.next == null) {
      return true;
    }
    Node n1 = head;// 慢指针 n1 -> mid
    Node n2 = head;// 快指针 n2 -> end
    while (n2.next != null && n2.next.next != null) {
      n1 = n1.next;
      n2 = n2.next.next;
    }
    // 慢指针来到中点或者上中点位置 快指针来到终点
    n2 = n1.next;// n2 来到右部分第一个结点
    n1.next = null;// n1.next -> null
    Node n3 = null;
    // 右部分反转
    while (n2 != null) {
      n3 = n2.next;// n3 -> save next node
      n2.next = n1;// next of right node convert
      n1 = n2;// n1 move
      n2 = n3;// n2 move
    }
    n3 = n1;// n3 -> save last node
    n2 = head;// n2 -> left first node
    boolean res = true;
    // 慢指针从右边开始 快指针从头结点开始 一一比较,任何一个为空就停下来
    while (n1 != null && n2 != null) {
      if (n1.value != n2.value) {
        res = false;
        break;
      }
      n1 = n1.next;// left to mid
      n2 = n2.next;// right to mid
    }
    n1 = n3.next;
    n3.next = null;
    // 将右部分逆序回来
    while (n1 != null) {
      n2 = n1.next;
      n1.next = n3;
      n3 = n1;
      n1 = n2;
    }
    return res;
  }
  public static void printLinkedList(Node head) {
    System.out.print("Linked List:");
    while (head != null) {
      System.out.print(head.value + " ");
      head = head.next;
    }
    System.out.println();
  }
  public static void main(String[] args) {
    Node head = null;
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(2);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(1);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(2);
    head.next.next = new Node(3);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(2);
    head.next.next = new Node(1);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(2);
    head.next.next = new Node(3);
    head.next.next.next = new Node(1);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(2);
    head.next.next = new Node(2);
    head.next.next.next = new Node(1);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
    head = new Node(1);
    head.next = new Node(2);
    head.next.next = new Node(3);
    head.next.next.next = new Node(2);
    head.next.next.next.next = new Node(1);
    printLinkedList(head);
    System.out.print(isPalindrome1(head) + " | ");
    System.out.print(isPalindrome2(head) + " | ");
    System.out.println(isPalindrome3(head) + " | ");
    printLinkedList(head);
    System.out.println("=========================");
  }
}
相关文章
|
9月前
|
机器学习/深度学习 算法
24. 两两交换链表中的节点, 19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交
1. **两两交换链表中的节点**:通过引入虚拟头结点,使所有节点都能采用统一的交换逻辑,避免对头结点单独处理。 2. **删除链表的倒数第N个节点**:利用双指针技巧,让快慢指针保持N个节点的距离,当快指针到达末尾时,慢指针正好指向待删除节点的前一个节点。 3. **链表相交**:先计算两链表长度并调整起点,确保从相同距离末尾的位置开始遍历,从而高效找到相交节点或确定无交点。 以上方法均在时间复杂度和空间复杂度上进行了优化,适合用于理解和掌握链表的基本操作及常见算法设计思路。
|
9月前
|
人工智能 前端开发 Java
Java 面试资料中相关代码使用方法与组件封装方法解析
这是一份详尽的Java面试资料代码指南,涵盖使用方法与组件封装技巧。内容包括环境准备(JDK 8+、Maven/Gradle)、核心类示例(问题管理、学习进度跟踪)、Web应用部署(Spring Boot、前端框架)、单元测试及API封装。通过问题库管理、数据访问组件、学习进度服务和REST接口等模块化设计,帮助开发者高效组织与复用功能,同时支持扩展如用户认证、AI推荐等功能。适用于Java核心技术学习与面试备考,提升编程与设计能力。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
183 6
Java 面试资料中相关代码使用方法与组件封装方法解析
|
11月前
|
人工智能 算法 数据库
美团面试:LLM大模型存在哪些问题?RAG 优化有哪些方法?_
美团面试:LLM大模型存在哪些问题?RAG 优化有哪些方法?_
|
Java 程序员 调度
Java 高级面试技巧:yield() 与 sleep() 方法的使用场景和区别
本文详细解析了 Java 中 `Thread` 类的 `yield()` 和 `sleep()` 方法,解释了它们的作用、区别及为什么是静态方法。`yield()` 让当前线程释放 CPU 时间片,给其他同等优先级线程运行机会,但不保证暂停;`sleep()` 则让线程进入休眠状态,指定时间后继续执行。两者都是静态方法,因为它们影响线程调度机制而非单一线程行为。这些知识点在面试中常被提及,掌握它们有助于更好地应对多线程编程问题。
564 9
|
安全 Java 程序员
Java面试必问!run() 和 start() 方法到底有啥区别?
在多线程编程中,run和 start方法常常让开发者感到困惑。为什么调用 start 才能启动线程,而直接调用 run只是普通方法调用?这篇文章将通过一个简单的例子,详细解析这两者的区别,帮助你在面试中脱颖而出,理解多线程背后的机制和原理。
766 12
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
562 4
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
存储 Java
HashMap之链表转红黑树(树化 )-treefyBin方法源码解读(所有涉及到的方法均有详细解读,欢迎指正)
本文详细解析了Java HashMap中链表转红黑树的机制,包括树化条件(链表长度达8且数组长度≥64)及转换流程,确保高效处理大量数据。
863 1
【数据结构】环形、相交、回文、分割、合并、反转链表
【数据结构】环形、相交、回文、分割、合并、反转链表
150 1
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
390 2