题目
判断一个单链表是否是回文链表。
如:[1, 2, 3, 2, 1],[1, 2, 3, 3, 2, 1] 就是一个回文链表,正着依次看链表中元素和反着依次看链表中元素都是一样的。
要求:
时间复杂度 O(n)
空间复杂度 O(1)
代码
package algorithm010;
import algorithm006.ListNode;
public class Algorithm010 {
public static void main(String[] args) {
System.out.println(isPalindromeLinkedList(getListNode1()));
System.out.println(isPalindromeLinkedList2(getListNode2()));
}
/**方法一
* 思路:特殊值记录 + 单链反转
* 在空间复杂度是O(1)的限制下
* 可以使用一个变量在遍历链表的过程中,sun += 该节点所对应的值,
* 需要设计的技巧在于这个值必须是特殊唯一的值
* 如算法中,我设定的对应的值就是用 sum = content * pos
* 如果链表是回文链表,那么从链表的最后一个元素反着来,依次减去节点相应的值
* 那么最后 sum 必然回归于 0,否则该链表就不是一个回文链表。
* 那么就在反转链表的遍历中,处理 sum 值的叠加
* 优点:思路比较清晰,简单,时间复杂度 O(2n),其实就是 O(n),写成O(2n)只不过是要与方法二有个比较
* 缺点:这个方法适用于content是整数的情况,如果是一个字符串,就不太合适了
*
* @param listNode
* @return
*/
public static boolean isPalindromeLinkedList(ListNode listNode) {
long sum = 0;
long factor = 1;
ListNode pre = null;
ListNode headNode = null;
while(listNode != null) {
sum += listNode.content * factor;
factor ++;
ListNode tempNext = listNode.next;
listNode.next = pre;
pre = listNode;
if(null == tempNext)
headNode = listNode;
listNode = tempNext;
}
factor = 1;
while(headNode != null) {
sum -= headNode.content * factor;
factor ++;
headNode = headNode.next;
}
return sum == 0;
}
/**方法二
* 思路:寻找中间节点,然后反转从中间节点后的链表,再与之前的链表比较
* 比较:遍历中间节点的过程中,同时遍历前半部分链表
* 如果在同一个循环中出现两个链表的content不同,那么就不成立是回文链表
* 注意:因为遍历的是后半部分反转后的链表,所以必须保证后半部分反转生成的链表长度必须不大于前半部分
* 难点1:如果寻找中间节点
* 在代码中利用了两种方式寻找中间节点,见 getMiddleHeadNodeBy2Length、getMiddleHeadNodeBy2Pointer
* 难点2:如果保证后半部分的节点的长度不大于前半部分
* 这里其实用实际链表来形容就是:
* 在 【0,1,2,3,2,1,0】,必须返回后半部分的节点 2
* 在 【0,1,2,3,3,2,1,0】,必须返回后半部分的节点 3
* 优点:getMiddleHeadNodeBy2Length 方法下 时间复杂度 O(2n)
* getMiddleHeadNodeBy2Pointer 方法下 时间复杂度 O(3/2n)
* 适用于所有链表
*
* @param listNode
* @return
*/
public static boolean isPalindromeLinkedList2(ListNode listNode) {
// ListNode middleHeadNode = getMiddleHeadNodeBy2Length(listNode);
ListNode middleHeadNode = getMiddleHeadNodeBy2Pointer(listNode);
System.out.println(middleHeadNode.toString());
ListNode reverseLinkedList = reverseLinkedList(middleHeadNode, null);
System.out.println(reverseLinkedList.toString());
while(reverseLinkedList != null) {
if(listNode.content != reverseLinkedList.content) {
return false;
}
listNode = listNode.next;
reverseLinkedList = reverseLinkedList.next;
}
return true;
}
/**
* 递归反转链表
* @param listNode
* @param pre
* @return
*/
private static ListNode reverseLinkedList(ListNode listNode, ListNode pre) {
if(listNode.next == null) {
listNode.next = pre;
return listNode;
}
ListNode tempNode = listNode.next;
listNode.next = pre;
pre = listNode;
listNode = tempNode;
return reverseLinkedList(listNode, pre);
}
/**
* 利用长度寻找中间节点
* @param listNode
* @return
*/
public static ListNode getMiddleHeadNodeBy2Length(ListNode listNode) {
int step = (int) Math.ceil(getLinkedListLength(listNode)/2f);//保证返回的是中间节点的坐标
//提前给 temp赋值,其实就是保证在下面的遍历结束后,temp返回的总是比中间节点前进一位
ListNode temp = listNode;
while(step > 0) {
if(null == temp)
temp = listNode;
else
temp = temp.next;
step --;
}
return temp;
}
/**
* 双指针寻找中间节点
* @param listNode
* @return
*/
public static ListNode getMiddleHeadNodeBy2Pointer(ListNode listNode) {
ListNode slowPointer = listNode;
ListNode fastPointer = listNode.next;
if(null == fastPointer)
return slowPointer;
while(slowPointer != null && fastPointer != null) {
slowPointer = slowPointer.next;
if(null == fastPointer.next) {
break;
}
fastPointer = fastPointer.next.next;
}
return slowPointer;
}
/**
* 获取链表长度
*
* @param listNode
* @return
*/
public static float getLinkedListLength(ListNode listNode) {
float length = 0;
while(listNode != null) {
length ++;
listNode = listNode.next;
}
return length;
}
public static ListNode getListNode1() {
ListNode listNode2 = new ListNode(0);
ListNode listNode4 = new ListNode(1);
ListNode listNode6 = new ListNode(2);
ListNode listNode8 = new ListNode(3);
ListNode listNode10 = new ListNode(3);
ListNode listNode11 = new ListNode(2);
ListNode listNode12 = new ListNode(1);
ListNode listNode13 = new ListNode(0);
listNode2.next = listNode4;
listNode4.next = listNode6;
listNode6.next = listNode8;
listNode8.next = listNode10;
listNode10.next = listNode11;
listNode11.next = listNode12;
listNode12.next = listNode13;
return listNode2;
}
public static ListNode getListNode2() {
ListNode listNode2 = new ListNode(0);
ListNode listNode4 = new ListNode(1);
ListNode listNode6 = new ListNode(2);
ListNode listNode8 = new ListNode(3);
ListNode listNode10 = new ListNode(2);
ListNode listNode11 = new ListNode(1);
ListNode listNode12 = new ListNode(0);
listNode2.next = listNode4;
listNode4.next = listNode6;
listNode6.next = listNode8;
listNode8.next = listNode10;
listNode10.next = listNode11;
listNode11.next = listNode12;
return listNode2;
}
}