回文链表
最近做算法题发现总是容易产生一种定势思维,或者说总是更倾向于先想到使用某些熟悉的数据结构或语法来解题,但往往从效果上来看时间复杂度和空间复杂度是大问题。
今天在力扣上看到了一道判断链表是否为回文链表的题目,如下所示:
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
最初想到的办法简单粗暴,将链表转字符串再翻转对比一下。然后试图想出一种不那么暴力的解法,也有可能是最近几天做了一些算法题的原因,灵光乍现想到了快慢指针,先找到中间节点,翻转后半段链表,再进行遍历对比... ...
讨厌翻转链表,看了一下参考题解发现其中有这种方案,也提到了这种方案在多线程环境下可能会出现问题,由于运算过程中会破坏链表,这个时候如果有其他线程需要访问链表,那就可能出问题。
第二种方案也果断放弃,由于短时间没有想出其他方案就参考了一下题解中的递归解法,初看有点迷,稍一思索明白了其中的含义,如果这里的单链表是一个数组的话,那么可以采用一种通俗易懂的双指针从数组两端开始遍历对比,但是,单链表不能从链表末端进行遍历,递归正是替代了末端指针的存在,果断决定自己按照理解到的思路写一遍。
首先,当节点不为null时,就直接进入下一个节点,直到节点为null,也就是递归到了末端节点的下一个节点,此时返回true。
在设计此处的时候又出现了一个误区,也算是定势思维,如果为null,应返回false。但实际上这个递归方法的返回值并不只代表节点是否为null,还有另一层含义是两节点是否相同,换句话说就是是否继续对比,还是直接返回结果。
所以,最终决定当节点为null时,返回true,算是便于后续的设计。
当判断下一次递归结果为true时,继续进行校验。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { ListNode curr = null; public boolean isPalindrome(ListNode head) { curr = head; return check(head); } public boolean check(ListNode node) { if (node == null) return true; if (check(node.next)) { if (curr.val == node.val) { curr = curr.next; return true; } } return false; } }
总的来讲代码不难,但在设计的时候仍旧觉得使用递归稍微有点绕,递归方法就像是一系列有关联的方法链,要注意下一次递归对上一次递归的影响,同时还要兼顾上一次递归在合适的位置调用下一次递归。