判断链表是否回文

判断链表是否回文,第1张

判断链表是否回文

题目:给出链表头结点,判断链表是否是回文结构

    
    public static class Node {

        public int value;

        public Node next;

        public Node(int value) {
            this.value = value;
        }
    }

方法1:借助Stack将整条链表压入栈中,然后从Stack中pop出元素和链表每一个元素逐个去做比较。时间复杂度为O(N),空间复杂度为O(N)。代码如下:

    
    public static boolean isPalindrome(Node head) {
        Stack stack = new Stack<>();
        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;
    }

方法2:将链表划分成左右两部分,让右部分入栈,然后用栈中的元素与左部分的每一个元素逐一去比对。时间复杂度为O(N),空间复杂度O(N),但是相比方法1,省了一点空间。代码如下:

 
    public static boolean isPalindrome(Node head) {
        if (head == null || head.next == null) { //链表的长度小于或等于1,那么一定是回文结构
            return true;
        }
        //slow指针,起始位置是2
        Node right = head.next;

        //fast指针,起始位置是1
        Node cur = head;

        while (cur.next != null && cur.next.next != null) { //保证fast指针每走2步,slow指针走1步
            right = right.next;
            cur = cur.next.next;
        }

        //此时right就是右部分的第一个节点
        Stack stack = new Stack<>();

        //将右部分的所有Node节点加入栈中
        while (right != null) {
            stack.push(right);
            right = right.next;
        }

        //将栈中元素d出,并与左部分Node节点逐一比对
        while (!stack.isEmpty()) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
    }

方法3:将链表分为左右部分。将右部分进行反转。若链表为 a->b->c->d ,左边部分为a->b,右边部分为c->d,那么反转后就是a->b<-c<-d。那么右边部分从d开始,左边部分从a开始,两个都往中间靠拢去比对。最后,恢复链表,返回结果。代码如下:

  
    public static boolean isPalindrome(Node head) {
        if (head == null || head.next == null) { //链表的长度小于或等于1
            return true;
        }
        //链表的长度大于1
        Node fast = head;
        Node slow = head;
        while (fast.next != null && fast.next.next != null) { //快指针每次走2步,慢指针每次走1步
            slow = slow.next;
            fast = fast.next.next;
        }
        //此时slow来到了上面例子的c点或b点
        Node rightPartNode = slow.next; //右部分第一个Node节点
        slow.next = null;  //断开左右部分的联系
        Node next = null;
        //反转链表
        while (rightPartNode != null) {
            next = rightPartNode.next;
            rightPartNode.next = slow;
            slow = rightPartNode;
            rightPartNode = next;
        }
        Node rightPartLastNode = slow; //此时的slow是右部分的最后一个元素 让rightPartLastNode也指向slow位置
        Node leftPartFirstNode = head; //leftPartFirstNode 用于保存左边第一个元素
        boolean res = true;
        while (rightPartLastNode != null && leftPartFirstNode != null) { //当且仅当来到y点时,退出循环,因为y点的next指针前面断了
            if (rightPartLastNode.value != leftPartFirstNode.value) {
                res = false;
                break;
            }
            rightPartLastNode = rightPartLastNode.next; //向左靠近y点
            leftPartFirstNode = leftPartFirstNode.next;  //向右靠近y点
        }
        //恢复链表
        //此时slow是右部分链表的最右边的元素
        Node cur = slow.next;
        slow.next = null;
        next = null;
        while (cur != null) {
            next = cur.next;
            cur.next = slow;
            slow = cur;
            cur = next;
        }
        return res;
    }

最后,伟子哥的小迷弟来句总结:

方法1和方法2均需要借助栈的数据结构,空间复杂度为O(N)。方法3在保证时间复杂度为O(N)的情况下,空间复杂度为O(1)。当然,方法3的代码也是最复杂的。当然我是希望面试和笔试场上,若空间复杂度没有要求的话,我个人推荐写第一种,毕竟很简单嘛。若要求空间复杂度为O(1),那没办法,只能平时多下功夫,把方法3看懂,练熟。数据结构和算法不仅仅是靠想,还要多动手去code。 Stay hungry stay foolish.

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5681801.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存