• 力扣-234-回文链表


    回文链表

    CategoryDifficultyLikesDislikes
    algorithmsEasy (52.70%)1576-
    Tags

    Companies


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

    示例 1:

    img

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

    示例 2:

    img

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

    提示:

    • 链表中节点数目在范围[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());
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
    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;
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
    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);
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
    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;
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
    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;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
  • 相关阅读:
    微信小程序 —— 基本结构
    Kafka原理剖析之「位点提交」
    爬虫入门基础:深入解析HTTP协议的工作过程
    详解:API开发【电商API封装商品数据SKU接口的开发接入】
    我的创作纪念日
    使用UiPath和AA构建的解决方案 5. 使用UiPath ReFramework处理采购订单
    MySQL中死锁
    STM32晶振的选择与计算
    易观分析联合中小银行联盟发布海南数字经济指数,敬请期待!
    数学建模【因子分析】
  • 原文地址:https://blog.csdn.net/weixin_44402395/article/details/128207033