算法面试题-回文链表

题目:

判断一个链表是否为回文结构。如果链表长度为N,时间复杂度为O(N),空间复杂度为O(1)

分析:

方法1:使用栈,需要O(N)的额外空间
1.将链表中节点的值全部存入栈中,
2.根据栈先进后出的特性,和原链表元素从头进行比较
方法2:使用栈和快慢指针,相较于方法1减少一半额外空间
1.使用快慢指针找到链表的中点(代码中找的是链表中点的下一个位置)
2.根据栈先进后出的特性,将链表后半部分存入栈中
3.将链表的前半部分和后半部分进行比较
方法3:使用快慢指针,并且将链表右半部分进行逆序,需要O(1)的额外空间
1.使用快慢指针找到链表的中点
2.将中点后的部分进行逆序操作
3.将逆序后的右半部分和左半部分进行比较

代码实现:

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

        public Node(int value) {
            this.value = value;
        }
    }
    
    // 需要O(N)的额外空间:使用栈
    public static boolean isPalindrome1(Node head) {
        Stack<Node> 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;

    }

    // 需要O(N)/2的额外空间:使用栈和快慢指针
        public static boolean isPalindrome2(Node head) {
        if (head == null || head.next == null) {
            return true;
        }
        Node n1 = head.next;
        Node n2 = head;

        // right用于寻找链表的中点下一个位置
        while (n2.next != null && n2.next.next != null) {
            n1 = n1.next;
            n2 = n2.next.next;
        }

        Stack<Node> stack = new Stack<>();

        while (n1 != null) {
            stack.push(n1);
            n1 = n1.next;
        }

        while (!stack.isEmpty()) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
    }

    // O(1)的额外空间:快慢指针并且将链表逆序
    public static boolean isPalindrome3(Node head) {
        if (head == null || head.next == null){
            return true;
        }

        // 快慢指针
        Node n1 = head;
        Node n2 = head;

        while (n2.next != null && n2.next.next != null){
            n1 = n1.next;  // n1指针移动到链表中点
            n2 = n2.next.next;  // n2指针移动到链表末端
        }

        n2 = n1.next;  // 此时n2指向右半部分的头结点
        n1.next = null;  //将n1指向的中点与右半部分断开

        Node n3 = null;
        // 将右半部分链表进行逆序操作
        while (n2 != null){
            n3 = n2.next;  // n3用于储存下一轮要调转指针的节点
            n2.next = n1;  // n2为此时需要调转指针的节点,将其指针往回指
            n1 = n2;  // 调转完n2的指针后,n2就变为下一个需要调转的节点指向的节点
            n2 = n3;  // 将n3赋给n2, 进行下一轮调转
        }

        n3 = n1; // 此时的n1是右半部分链表的最后一个节点,使用n3对其进行保持以便后续恢复链表
        n2 = head;  // 将n2指向左半部分的头节点

        boolean res = true;
        // 对左半部分和逆序后的右半部分进行比较
        while (n1 != null && n2 != null){
            if (n1.value != n2.value){
                res = false;
                break;
            }
            n1 = n1.next;  // left->mid
            n2 = n2.next;  // right->mid
        }

        // 恢复右半部分
        n1 = n3.next;  // n1指向右半部分倒数第二个节点
        n3.next = null;  // 将右半部分最后一个节点断开
        while (n1 != null){
            n2 = n1.next;
            n1.next = n3;
            n3 = n1;
            n1 = n2;
        }
        return res;
    }
}

tips:

方法2和方法3快慢指针的初始化有所不同,需要根据题目的不同进行变化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值