【算法】判断一个链表是否为回文结构

2018-06-22  本文已影响0人  mapleYe

题目

给定一个链表的头节点head,请判断该链表是否为回 文结构。
例如: 1->2->1,返回true。
1->2->2->1,返回true。
15->6->15,返回true。
1->2->3,返回false。
进阶:
如果链表长度为N,时间复杂度达到O(N),额外空间复杂 度达到O(1)。

链表结构如下

  public static class Node {
    public Node next;
    public int value;

    public Node(int data) {
      value = data;
    }
  }

1、基础版--空间复杂度O(N)

思路:

使用栈,将链表的所有数据入栈。然后链表从头开始遍历,每次指针指向下一个节点时,栈弹出,判断两者是否相等,直至栈空都相等的话,那么就是回文链表。

代码实现

public static boolean isPalindromeList1(Node head) {
    Stack<Node> stack = new Stack<Node>();
    Node p = head;
    while(p != null) {
      stack.push(p);
      p = p.next;
    }
    while(head != null) {
      Node node = stack.pop();
      if (node.value != head.value) {
        return false;
      }
      head = head.next;
    }
    return true;
  }

2、进阶版1--空间复杂度O(N/2)

思路

回顾上面的解法,其实我们只需比较到链表的中点即可。因此,我们不再对链表全部入栈,而是先找到链表的中点,然后从中点开始入栈。最后只需比较链表~中点之间的数据和链表是否一一相等即可

算法实现

  public static boolean isPalindromeList2(Node head) {
    Node right = head.next;
    Node cur = head.next.next;
    // 找到中点节点
    while(cur.next != null && cur.next.next != null) {
      right = right.next;
      cur = cur.next.next;
    }
    // 从中间回文开始的节点入栈
    Stack<Node> stack = new Stack<Node>();
    while(right != null) {
      System.out.println(right.value);
      stack.push(right);
      right = right.next;
    }
    // 比较前回文和后回文部分
    while(!stack.isEmpty()) {
      Node node = stack.pop();
      if (node.value != head.value) {
        return false;
      }
      head = head.next;
    }
    return true;
  }

这里需要提到中点链表的查找方法:
1、准备一个慢指针,一次走1格
2、准备一个快指针,一次走2格
3、当快指针走完了,那么慢指针所指向的节点就是中点节点。若不理解,自己画图理解理解

3、进阶版2--空间复杂度O(1)

思路

修改链表的指向,将回文部分链表逆转指向中点,例如:
1 -> 2 -> 3 -> 2 -> 1改为
1 -> 2 -> 3 <- 2 <- 1 这种结构判断
因此,算法步骤如下:
1、我们需要先找到中点节点,然后修改尾节点~中点节点的指向,翻转节点
2、首尾指针开始遍历链表,直至首尾的指针抵达中点,期间判断首尾指针指向的value是否相等
3、还原链表原来的结构(如果要求不能修改链表结构的话,就需要做这步骤,算是可选步骤吧)

算法实现

  public static boolean isPalindromeList3(Node head) {
    Node right = head.next;
    Node cur = head.next.next;
    // 找到中间节点
    while(cur.next != null && cur.next.next != null) {
      right = right.next;
      cur = cur.next.next;
    }
    // 右边第一个节点
    Node n1 = right.next;
    // 使中间节点指向null
    right.next = null;
    Node pre = null;
    Node next = null;
    // 以中间开始,翻转节点
    while(n1 != null) {
      next = n1.next;
      n1.next = pre;
      pre = n1;
      n1 = next;
    }
    // 右边部分的head节点
    Node rightHead = pre;
    Node leftHead = head;
    // 备份最后的指针
    Node lastNode = pre;
    boolean res = true;
    while(leftHead != null && rightHead != null) {
      if(leftHead.value != rightHead.value) {
        res = false;
        break;
      }
      leftHead = leftHead.next;
      rightHead = rightHead.next;
    }
    // 恢复原来的链表结构,可选步骤
    n1 = lastNode;
    pre = null;
    next = null;
    while(n1 != null) {
      next = n1.next;
      n1.next = pre;
      pre = n1;
      n1 = next;
    }
    // 将中间节点重新指向翻转后的最后节点
    right.next = pre;
    return res;
  }

上一篇下一篇

猜你喜欢

热点阅读