力扣-234-回文链表

回文链表

CategoryDifficultyLikesDislikes
algorithmsEasy (52.70%)1576-
Tags

Companies


给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

示例 1:

img

输入:head = [1,2,2,1]
输出:true

示例 2:

img

输入:head = [1,2]
输出:false

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

知识点补充:

  1. 有两种常用的列表实现,分别为数组和链表。
    1. 数组列表底层是使用数组存储值,可以通过索引再O(1)的时间内访问列表的任何位置的值,这是由基于内存寻址的方式。
    2. 链表存储的是称为节点的对象,每个节点保存一个值和指向下一个节点的指针。访问某个特定索引的节点需要O(n)的时间,因为要通过指针获取到下一个位置的节点。
  2. 确定数组列表是否为回文很简单,可以使用双指针来比较两端的元素,并向中间移动,而将链表的值赋值到数组列表中是O(n),因此最简单的方法就是将链表的值复制到数组列表中,再使用双指针法判断。

思路

  1. 看了网上很多双指针,数组,栈,哈希算法计算的,都觉得大题小作了,一下看到一个让人眼前一亮的方法,记录一下。

    1. 就是通过StringBuilder类,将链表中的值存储再一个StringBuilder中,然后使用equals方法计算stringBuilder和stringBuilder.resverse()的值,结果就是答案。

      class Solution {
          public boolean isPalindrome(ListNode head) {
              StringBuilder stringBuilder = new StringBuilder();
              while (head != null) {
                  stringBuilder.append(head.val);
                  head = head.next;
              }
              return stringBuilder.toString().equals(stringBuilder.reverse().toString());
          }
      }
      
  2. 虽然按照上述问题能解决,但是还是得看一下使用不同的算法实现的方式。

    1. 将head链表的值存储在数组中,然后判断。

      class Solution {
          public boolean isPalindrome(ListNode head) {
              List<Integer> vals = new ArrayList<Integer>();
              // 将链表的值复制到数组中
              ListNode currentNode = head;
              while (currentNode != null) {
                  vals.add(currentNode.val);
                  currentNode = currentNode.next;
              }
              // 使用双指针判断是否为回文数
              int front = 0;
              int back = vals.size() - 1;
              while (front < back) {
                  if (!vals.get(front).equals(vals.get(back))) {
                      return false;
                  }
                  front++;
                  back--;
              }
              return true;
          }
      }
      
  3. 递归的方法

    1. 如果使用递归反向迭代节点,同时使用递归函数外的变量向前迭代,就可以判断链表是否为回文。

    2. 指针是先到尾节点,由于递归的特性再从前往后进行比较。frontPointer是递归方法外的指针。若currentNode.val != frontPoint.val 则返回false。反之,frontPinter向前移动并返回true。

    3. 算法的正确性在于递归处理节点的顺序是相反的(回顾上面打印的算法),而我们再函数外又记录了一个变量,因此从本质上,我们同时在正向和逆向迭代匹配。

      class Solution {
          private ListNode frontPointer;
      
          private boolean recursivelyCheck(ListNode currentNode) {
              if (currentNode != null) {
                  if (!recursivelyCheck(currentNode.next)) {
                      return false;
                  }
                  if (currentNode.val != frontPointer.val) {
                      return false;
                  }
                  frontPointer = frontPointer.next;
              }
              return true;
          }
      
          public boolean isPalindrome(ListNode head) {
              frontPointer = head;
              return recursivelyCheck(head);
          }
      }
      
  4. 快慢指针

    1. 可以将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。比较完成后我们应该将链表回复原样。

    2. 步骤:

      1. 找到前半部分链表的尾节点。
      2. 反转后半部分链表。
      3. 判断是否为回文。
      4. 恢复链表。
      5. 返回结果。
    3. 可以根据快慢指针进行判断,找到中间值(前半部分链表的尾节点)。

      class Solution {
          public boolean isPalindrome(ListNode head) {
              if (head == null) {
                  return true;
              }
              // 找到前半部分链表的尾节点
              ListNode firstHalfEnd = endOfFirstHalf(head);
              ListNode secondHalfStart = reverseList(firstHalfEnd.next);
              // 判断是否为回文
              ListNode p1 = head;
              ListNode p2 = secondHalfStart;
              boolean result = true;
              while (result && p2 != null) {
                  if (p1.val != p2.val) {
                      result = false;
                  }
                  p1 = p1.next;
                  p2 = p2.next;
              }
              // 还原链表并返回结果
              firstHalfEnd.next = reverseList(secondHalfStart);
              return result;
          }
      
          private ListNode reverseList(ListNode head) {
              ListNode prev = null;
              ListNode curr = head;
              while (curr != null) {
                  ListNode nextTemp = curr.next;
                  curr.next = prev;
                  prev = curr;
                  curr = nextTemp;
              }
              return prev;
          }
      
          private ListNode endOfFirstHalf(ListNode head) {
              ListNode fast = head;
              ListNode slow = head;
              while (fast.next != null && fast.next.next != null) {
                  fast = fast.next.next;
                  slow = slow.next;
              }
              return slow;
          }
      }
      
  5. 哈希法也是比较优秀的,这里复制粘贴代码,我觉得实现起来逻辑比较复杂,可能是还没太多关于哈希计算的知识点积累,后续再来回顾。

    class Solution {
        public boolean isPalindrome(ListNode head) {
            ListNode t=head;
    		int base = 11, mod = 1000000007;
        	int left = 0, right = 0, mul = 1;
            while(t!=null){
            	left = (int) (((long) left * base + t.val) % mod);
            	right = (int) ((right + (long) mul * t.val) % mod);
                mul = (int) ((long) mul * base % mod);
                t=t.next;
            }
            return left==right;
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NoSuchManException

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值