Loading

力扣 - 234.回文链表

题目

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

思路1

  • 可以利用首位指针,一个从前往后,一个从后往前,直到相遇,由于数组的访问速度快,于是先将链表转化为数组,再通过索引进行访问比较
  • 要注意的是,如果用ArrayList要用equals来比较,不能用 == 来比较,因为Integer-128~127之间的数字比较是相等的,指向同一个对象,而超出此范围,虽然数字一样,但是对象的地址不一样
  • 时间复杂度是O(n),空间复杂度是O(n)

代码实现

import java.util.List;
import java.util.ArrayList;
class Solution {
    public boolean isPalindrome(ListNode head) {
        //因为存储得是Integer,超过-128~127就会创建新的对象
        List<Integer> list = new ArrayList<>();
        
        while (head != null) {
            list.add(head.val);
            head = head.next;
        }

        int front = 0;
        int back = list.size() - 1;

        //一直比较到相遇
        while (front < back) {
            if (!list.get(front).equals(list.get(back))) {
                return false;
            }
            front++;
            back--;
        }
        return true;
    }
}

思路2(最高效)

  • 利用快慢指针,slow慢指针走一步,fast快指针走两步,找到中间结点,然后翻转链表的后半部分,然后再同时进行比较链表的后半部分和前半部分是否相同
  • 最好将反转后的链表在复原回去
  • 时间复杂度是O(n),空间复杂度是O(1)

代码实现

class Solution {
    public boolean isPalindrome(ListNode head) {
        //如果输入的是空,则直接返回true
        if (head == null) {
            return true;
        }
        //获取链表前半部分的最后一个结点
        ListNode firstHalfEnd = endOfFirstHalf(head);
        ListNode secondHaofStart = reverseList(firstHalfEnd.next);

        //利用双指针同时进行比较
        ListNode p1 = head;
        ListNode p2 = secondHaofStart;
        while (p2 != null) {
            if (p1.val != p2.val) {
                return false;
            }
            p1 = p1.next;
            p2 = p2.next;
        }

        //将链表复原
        firstHalfEnd.next = reverseList(secondHaofStart);
        return true;
    }

    /**
     * 获取前半部分的最后一个结点
     */
    public ListNode endOfFirstHalf(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
	/**
     * 获取反转链表后的新的首节点
     */
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode n = cur.next;
            cur.next = pre;
            pre = cur;
            cur = n;
        }
        return pre;
    }
}

思路3

  • 利用递归来解决,时间复杂度为O(n),不过空间复杂度也是O(n)
  • 空间复杂度:O(n),其中 n 指的是链表的大小。我们要理解计算机如何运行递归函数,在一个函数中调用一个函数时,计算机需要在进入被调用函数之前跟踪它在当前函数中的位置(以及任何局部变量的值),通过运行时存放在堆栈中来实现(堆栈帧)。在堆栈中存放好了数据后就可以进入被调用的函数。在完成被调用函数之后,他会弹出堆栈顶部元素,以恢复在进行函数调用之前所在的函数。在进行回文检查之前,递归函数将在堆栈中创建 n 个堆栈帧,计算机会逐个弹出进行处理。所以在使用递归时要考虑堆栈的使用情况。

代码实现

class Solution {

    ListNode preNode;

    public boolean isPalindrome(ListNode head) {
        preNode = head;
        return recursion(head);
    }
    
    public boolean recursion(ListNode curNode) {
        if (curNode != null) {
            // 先一直寻找到末尾,然后开始返回值一步步回溯回来
            if (!recursion(curNode.next)) return false;
            // 在回溯过程中仅仅判断值是否相等,如果不相等,返回false,
            // 那么递归到的上一级中的!recursion(curNode.next)就会判定为ture,然后一直返回false,
            // 表的头部,然后最后返回的就是false,即非回文
            if (preNode.val != curNode.val) return false;
            preNode = preNode.next;
        }
        // 否则如果是空链表或者到达末尾就开始返回true
        return true;
    }
}

思路4

  • 使用栈,利用栈先进后出的特性,将元素一个一个顺序入栈,在出栈,就可以完成从后往前的遍历
  • 不过这种算法所用的时间复杂度和空间复杂度和思路1是一样的,因为都是复制链表然后使用首位指针进行比较

代码实现

import java.util.LinkedList;
class Solution {
    public boolean isPalindrome(ListNode head) {
        LinkedList<Integer> stack = new LinkedList<>();
        ListNode p = head;
        //定义count计算链表长度
        int count = 0;
        //同时将元素入栈
        while (p != null) {
            stack.push(p.val);
            p = p.next;
            count++;
        }

        //中点位置为count / 2
        for (int i = 0; i < count / 2; i++) {
            if (stack.pop() != head.val) {
                return false;
            }
            head = head.next;
        }

        return true;
    }
}
posted @ 2020-10-21 11:20  linzeliang  阅读(99)  评论(0)    收藏  举报