链表15:回文链表的7种解法
Posted 纵横千里,捭阖四方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表15:回文链表的7种解法相关的知识,希望对你有一定的参考价值。
这是我们线性表专题的最后一篇了,加油!
LeetCode234题,请判断一个链表是否为回文链表。这个题不难,笔者面美团时第一轮就写了,我当时心中一阵窃喜,很快就完成了,结果面试官又给我加了一道。
示例1:
输入: 1->2
输出: false
示例2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
看到这个题你有几种思路解决,经过前面这些题目的蹂躏,我现在看到这个题瞬间就想到6种解法。虽然有几个是相同的,但是仍然可以是新的方法。
方法1:将链表元素都赋值到数组中,然后可以从数组两端向中间对比。
方法2:将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较,只要有一个不相等,那就不是回文链表了。
方法3:上面方法的改造,先遍历第一遍,得到总长度。之后一遍历链表,一遍压栈。当到达链表长度一半的位置之后,就不再压栈,而是一边出栈,一遍遍历,一遍比较,只要有一个不相等,就不是回文链表。
方法4:反转链表法, 先创建一个链表newList,然后原始链表oldList的元素值逆序保存到newList中,然后重新遍历newList和oldList,同时比较元素的值,只要有一个位置的元素值不一样,就不是回文链表。
方法5:将4进行优化,我们其实只反转一半的元素就行了。步骤是:先遍历一遍链表,得到长度,然后重新遍历链表,一边遍历,一边将链表反转。之后到达一半的位置后,就不再反转,而是比较两个链表,只要有一个元素不一样,就不是回文链表。
方法6:还是对4的改进,我们使用快慢指针 ,fast一次走两步,slow一次走一步。当fast到达表尾的时候,slow正好到达一半的位置,那么接下来可以从头开始逆序一半的元素,或者从slow开始逆序一半的元素,都可以。
方法7:递归法,这个我们下面单独解释。
上面这些方式,1.即使你说了,面试官,一般也不会让你写。2和3,如果能写出来,算及格,但是面试官问这个题一般就想考察你的链表操作能力,所以链表反转是绕不过去的问题。
但是这里我们尽量不要真的将原始链表给改了,在创建新链表的时候,采用头插法就行了,也就是每次都将要逆序的元素添加到新链表的head后面。
1.快慢指针法
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) {
return true;
}
ListNode slow = head, fast = head;
ListNode pre = head, prepre = null;
while(fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
pre.next = prepre;
prepre = pre;
}
if(fast != null) {
slow = slow.next;
}
while(pre != null && slow != null) {
if(pre.val != slow.val) {
return false;
}
pre = pre.next;
slow = slow.next;
}
return true;
}
2.使用栈的两种方法
将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较,只要有一个不相等,那就不是回文链表了。代码:
public boolean isPalindrome(ListNode head) {
ListNode temp = head;
Stack<Integer> stack = new Stack();
//把链表节点的值存放到栈中
while (temp != null) {
stack.push(temp.val);
temp = temp.next;
}
//然后再出栈
while (head != null) {
if (head.val != stack.pop()) {
return false;
}
head = head.next;
}
return true;
}
上面方法的改造,先遍历第一遍,得到总长度。之后一遍历链表,一遍压栈。当到达链表长度一半的位置之后,就不再压栈,而是一边出栈,一遍遍历,一遍比较,只要有一个不相等,就不是回文链表。代码就是这样:
public boolean isPalindrome(ListNode head) {
if (head == null)
return true;
ListNode temp = head;
Stack<Integer> stack = new Stack();
//链表的长度
int len = 0;
//把链表节点的值存放到栈中
while (temp != null) {
stack.push(temp.val);
temp = temp.next;
len++;
}
//len长度除以2
len >>= 1;
//然后再出栈
while (len-- >= 0) {
if (head.val != stack.pop())
return false;
head = head.next;
}
return true;
}
3.递归法解决
我们知道,如果对链表逆序打印可以这样写:
private void printListNode(ListNode head) {
if (head == null)
return;
printListNode(head.next);
System.out.println(head.val);
}
也就是说最先打印的是链表的尾结点,是从后往前打印的,如果以后谁再给你说单向链表不能从后往前遍历,你就甩出这段代码。看到这里是不是有灵感了,我们来对上面的对面进行改造一下:
ListNode temp;
public boolean isPalindrome(ListNode head) {
temp = head;
return check(head);
}
private boolean check(ListNode head) {
if (head == null)
return true;
boolean res = check(head.next) && (temp.val == head.val);
temp = temp.next;
return res;
}
其他几种方式主要是开拓思路的,我们就不写了,感兴趣的话,请读者自行尝试一下。
以上是关于链表15:回文链表的7种解法的主要内容,如果未能解决你的问题,请参考以下文章