题目描述
LeetCode234,这是一个简单且经典的链表题,判断一个链表 是否为回文链表
示例1
输入:1 -> 2 -> 2 ->1
输出:true
方法分析
方法1:将链表元素全部压入栈(栈的特点:先进后出,将元素全部压入栈后就能达到链表最后一个元素最先出栈的效果),然后一边出栈,一边重新遍历链表 ,并且一边比较出栈的元素和链表的元素是否相等。如果完全相等,就说明该链表是一个回文序列的链表,返回true;但只要有一个元素不相等,就会返回false
代码实现
/**
* 判断是否为回文序列
* @param head 头节点
* @return 是回文序列/不是回文序列
*/
public boolean isPalindrome(Node head){
Node temp = head;
Stack<Integer> stack = new Stack<>();
// 将单链表全部压入栈
while (temp != null){
stack.push(temp.data);
temp = temp.next;
}
// 一边出栈,一边比较
while (head != null){
if (head.data != stack.pop()){
return false;
}
head = head.next;
}
return true;
}
测试运行
主方法
public static void main(String[] args) {
// 将一个数组转换为链表
int [] arr = {1,2,2,1};
SingleLinkedList linkedList = new SingleLinkedList();
Node head =SingleLinkedList.transformSingleList(arr);
System.out.println("原始链表为:");
linkedList.printSingleList(head);
System.out.println();
PalindromicSequence sequence = new PalindromicSequence();
boolean palindrome = sequence.isPalindrome(head);
System.out.println("palindrome = " + palindrome);
}
节点代码
public class Node {
public int data;
public Node next;
public Node(int data) {
this.data = data;
}
}
数组转链表代码
/**
* 将数组转为链表
* @param arr 数组
* @return 链表头节点
*/
public static Node transformSingleList(int [] arr){
Node head =new Node(arr[0]);
// 使用辅助节点遍历将其余的元素 ,将其变为节点,添加在头节点之后
Node node = head;
for (int i = 1 ; i < arr.length ;i ++) {
Node temp = new Node(arr[i]);
node.next = temp;
node = temp;
}
return head;
}
遍历链表代码
/**
* 链表遍历
* @param head 链表头节点
*/
public void printSingleList(Node head){
Node node = head;
while (node != null){
System.out.print(node.data + "->");
node = node.next;
}
}
测试结果
原始链表为:
1->2->2->1->
palindrome = true
优化思路
思路1:先遍历一遍链表,得到链表总长度。之后一边遍历,一遍压入栈。达到链表长度一半时就不再压栈,而是一边出栈,一边遍历,一边比较,只要有一个不相等,就不是回文序列。这样可以节省一半的空间
代码实现
/**
* 判断是否为回文序列
* @param head 头节点
* @return 是回文序列/不是回文序列
*/
public boolean isPalindrome1(Node head){
SingleLinkedList linkedList = new SingleLinkedList();
Node temp = head;
int length = linkedList.getLinkedListLength(temp);
Stack<Integer> stack = new Stack<>();
// 将单链表一半压入栈
while (temp != null || stack.size()<=length/2){
stack.push(temp.data);
temp = temp.next;
}
// 一边出栈,一边比较
while (head != null){
if (head.data != stack.pop()){
return false;
}
head = head.next;
}
return true;
}
思路2:既然要得到长度,那还是要遍历一次链表才可以。那是不是可以一边遍历一边全部压栈,然后第二遍比较的时候,只比较一半的元素呢?也就是说,只有一半的元素出栈,链表也只遍历一半。
其他思路1
反转链表法:先创建一个链表newList,将原始链表oldList的元素值逆序保存到newList中,然后重新一边遍历两个链表,一边比较元素的值,只要有一个位置的元素不一样,就不是回文链表。
代码实现
/**
* 使用双指针 + 链表实现链表反转
* @param head 链表头节点
* @return 是否是回文序列
*/
public boolean isPalindrimucByReverseList(Node head){
if (head == null || head.next == null){
return false;
}
// 定义快慢指针
Node fast = head;
Node slow = head;
// 定义反转后的链表头
Node pre = head;
// 定义反转需要的临时头
Node temp = null;
while (fast != null && fast.next != null){
pre = slow;
// 双指针移动
slow = slow.next;
fast = fast.next.next;
// 链表前半段反转
pre.next = temp;
temp = pre;
}
// 判断奇偶性,如果是偶数,不用这步,如果是奇数,执行这步
if (fast != null){
slow = slow.next;
}
while (pre != null && slow != null){
if (pre.data != slow.data){
return false;
}
pre = pre.next;
slow = slow.next;
}
return true;
}
反转链表法的优化
方法1:只反转一半的元素就行: 先遍历一边,得到链表的总长度,然后重新遍历,达到一半的位置后就不再反转,开始比较两个链表
方法2:使用双子针思想里的快慢指针:fast一次走两步,slow一次走一步。当fast到达表尾的时候,slow正好达到一半的位置,那么接下来就可以从头开始逆序一半的元素,或者从slow开始逆序一半的元素,都可。
其他思路2
在遍历的时候是用递归来反转一半链表 等等