題解:java
判斷一個鏈表是否是迴文的,這裏要求O(n)時間複雜度和O(1)的空間時間複雜度,總共想了三種辦法,三種辦法都用到了兩個指針,符合題目要求的只有最後一種。node
第一種辦法:用數組倒着存前半段的鏈表的值,而後和後半段鏈表的值進行比較。這種解法運行的時間最久多是由於數組倒着插入比較耗時。數組
代碼:數據結構
[java] view plain copyapp
- //用數組實現 o(n/2)空間
- public static boolean isPalindrome(ListNode head) {
-
- // ArrayList<Integer> nodeVal=new ArrayList<>();
- LinkedList<Integer> nodeVal=new LinkedList<>();
-
- if(head==null||head.next==null)
- return true;
- ListNode slow=head;
- ListNode fast=head;
-
- nodeVal.add(0,slow.val);
- while(fast.next!=null&&fast.next.next!=null)
- {
- fast=fast.next.next;
- slow=slow.next;
- nodeVal.add(0,slow.val);
- }
-
- ListNode cur=slow;
- if(fast.next!=null)//鏈表長度爲偶數
- cur=slow.next;
- int i=0;
- while(cur!=null)
- {
- if(nodeVal.get(i)!=cur.val)
- return false;
- cur=cur.next;
- i++;
- }
- return true;
- }
-
第二種解法:在第一種的思路的基礎上,咱們要實現一個倒序,咱們幹嗎不用現成的數據結構-棧,因而把鏈表前半段壓棧,而後出棧和後面的鏈表依次比較,這種運行時間最短,但由於用到了棧仍是不符合題目要求。函數
代碼:ui
[java] view plain copyspa
- //用棧實現
- public static boolean isPalindrome2(ListNode head) {
-
- Stack<ListNode> stack=new Stack<>();
- ListNode slow=head;
- ListNode fast=head;
-
- if(fast==null||fast.next==null)//0個節點或是1個節點
- return true;
-
- stack.push(slow);
- while(fast.next!=null&&fast.next.next!=null)
- {
-
- fast=fast.next.next;
- slow=slow.next;
- stack.push(slow);
- }
- if(fast.next!=null)//鏈表長度爲偶數
- slow=slow.next;
-
- ListNode cur=slow;
- while(cur!=null)
- {
- if(cur.val!=stack.pop().val)
- return false;
- cur=cur.next;
- }
- return true;
-
- }
第三種:咱們這樣想,咱們可不能夠不借助外在的存儲實現倒序呢,實際上是能夠的,鏈表反轉的時候咱們就沒有藉助外在存儲。思路是把後半段的原地鏈表反轉而後和前半段進行比較(固然你也能夠反轉前半段)運行時間稍微比第二種慢一些,可是符合題目O(1)空間複雜度的要求.net
代碼:指針
[java] view plain copy
- //鏈表原地轉置實現o(1)空間複雜度
- public static boolean isPalindrome3(ListNode head) {
- ListNode slow=head;
- ListNode fast=head;
-
- if(fast==null||fast.next==null)//0個節點或是1個節點
- return true;
-
-
- while(fast.next!=null&&fast.next.next!=null)
- {
- fast=fast.next.next;
- slow=slow.next;
- }
- //對鏈表後半段進行反轉
- ListNode midNode=slow;
- ListNode firNode=slow.next;//後半段鏈表的第一個節點
- ListNode cur=firNode.next;//插入節點從第一個節點後面一個開始
- firNode.next=null;//第一個節點最後會變最後一個節點
- while(cur!=null)
- {
- ListNode nextNode=cur.next;//保存下次遍歷的節點
- cur.next=midNode.next;
- midNode.next=cur;
- cur=nextNode;
- }
-
- //反轉以後對先後半段進行比較
- slow=head;
- fast=midNode.next;
- while(fast!=null)
- {
- if(fast.val!=slow.val)
- return false;
- slow=slow.next;
- fast=fast.next;
- }
- return true;
-
- }
快行指針找到鏈表中間結點
1. 反轉前半部分看是否和後半部分同樣
2. 將前半部分入棧,迭代訪問剩下的一半結點,每次的棧頂元素同樣則是迴文鏈表
[java] view plain copy
- import java.util.Stack;
- public class isHuiWen {
- public boolean isPalinddrome(LinkedListNode head) {
- LinkedListNode fast = head;
- LinkedListNode slow = head;
-
- Stack<Integer> stack = new Stack<Integer>();
-
- while( fast != null && fast.next != null ) {
- stack.push(slow.data);
- slow = slow.next;
- fast = fast.next.next;
- }
-
- //若是鏈表有奇數個元素,那麼fast這時不爲空,則比較後半段時跳過中間元素
- if ( fast != null ) {
- slow = slow.next;
- }
-
- while (slow != null) {
- int top = stack.pop().intValue();
- //若是不相同,則不是迴文
- if (top != slow.data) {
- return false;
- }
- slow= slow.next;
- }
- return true;
- }
- }
遞歸的解法:
[java] view plain copy
- class Result{
- public LinkedListNode node;
- public boolean result;
- }
-
- Result isPalindromeRecurse(LinkedListNode head, int length) {
- if (head == null || length == 0) {
- return new Result(null, true);
- }
- else if(length == 1) {
- return new Result(head.next,true);
- }
- else if(length == 2) {
- return new Result(head.next.next, head.data == head.next.data);
- }
- Result res = isPalindromeRecurse(head.next, length -2);
- if(!res.result || res.node == null){
- return res;
- }
- else{
- res.result = head.data == res.node.data;
- res.node = res.node.next;
- return res;
- }
- }
-
- boolean isPalinddrome(LinkedListNode head) {
- Result p = isPalindromeRecurse(head, listSize(head));
- return p.result;
- }
思路:
1 Iterative,利用棧,把鏈表前半段存入棧,再逐個彈棧和鏈表後半段比較。注意鏈表長度爲奇數的狀況!要跳過中間節點!
2 遞歸!
定義遞歸函數爲 Result rec(LinkedListNode head, int length) 意義爲 傳入鏈表頭結點和鏈表長度,返回該鏈表的尾節點的下一個節點
Result是一個Wrapper 類,包含了node和當前是否match的判斷
遞歸的關鍵是要清楚遞歸函數的每個參數的意義是什麼,還有返回值的意義是什麼!!!
以下圖:假設在遞歸的某一個階段,要比較前半段的那個1和後半段的那個1是否相等:
根據遞歸能夠獲得藍色部分的返回值Result,包含了一個res.node和match值。res.node的意義就是該子鏈表的尾節點的下一個節點,即後半段的1!
而後咱們能夠把head指向的1和res.node指向的1比較。若是相同則設置match爲true,而且更新本層遞歸(紅色區域)的返回值爲本層子鏈表的尾節點(1)的下一個節點(0),做爲res.node返回。
[java] view plain copy
- package LinkLists;
-
- import java.util.Stack;
-
- import CtCILibrary.LinkedListNode;
-
- public class S2_7 {
-
- // 利用棧,把鏈表前半段存入棧,再逐個彈棧和鏈表後半段比較
- public static boolean isPalindrome(LinkedListNode head) {
- LinkedListNode fast = head;
- LinkedListNode slow = head;
-
- Stack<Integer> stack = new Stack<Integer>();
-
- while (fast != null && fast.next != null) {
- stack.push(slow.data);
- slow = slow.next;
- fast = fast.next.next;
- }
-
- if (fast != null) { // 只有當鏈表長度爲奇數時,fast纔不會爲null
- slow = slow.next; // 這時要跳過中間節點
- }
-
- while (slow != null) { // 邊彈棧邊比較
- int top = stack.pop().intValue();
- if (top != slow.data) {
- return false;
- }
- slow = slow.next;
- }
- return true;
- }
-
- // 遞歸
- public static boolean isPalindrome2(LinkedListNode head) {
- int size = 0;
- LinkedListNode n = head;
- while (n != null) {
- size++;
- n = n.next;
- }
- Result p = rec(head, size);
- return p.match;
- }
-
- // 傳入鏈表頭結點和鏈表長度,返回該鏈表的尾節點的下一個節點
- public static Result rec(LinkedListNode head, int length) {
- if (head == null || length == 0) { // 空鏈表,確定是迴文
- return new Result(null, true);
- } else if (length == 1) { // 只有1個節點,確定是迴文
- return new Result(head.next, true);
- } else if (length == 2) { // 有兩個節點,若是相同則是迴文
- return new Result(head.next.next, head.data == head.next.data);
- }
-
- Result res = rec(head.next, length-2); // 長度縮小2的子鏈表問題,res存放子問題的結果和子鏈表尾節點的下一個節點
- if(!res.match || res.node==null) { // 不match
- return res;
- } else{
- res.match = head.data == res.node.data; // 比較當前節點和尾節點是否相等
- res.node = res.node.next; // 更新返回值,即該鏈表的尾節點的下一個節點
- return res;
- }
- }
-
- static class Result {
- public LinkedListNode node;
- public boolean match;
- public Result(LinkedListNode n, boolean res) {
- node = n;
- match = res;
- }
- }
-
-
- public static void main(String[] args) {
- int length = 10;
- LinkedListNode[] nodes = new LinkedListNode[length];
- for (int i = 0; i < length; i++) {
- nodes[i] = new LinkedListNode(i >= length / 2 ? length - i - 1 : i, null, null);
- }
-
- for (int i = 0; i < length; i++) {
- if (i < length - 1) {
- nodes[i].setNext(nodes[i + 1]);
- }
- if (i > 0) {
- nodes[i].setPrevious(nodes[i - 1]);
- }
- }
- // nodes[length - 2].data = 9; // Uncomment to ruin palindrome
-
- LinkedListNode head = nodes[0];
- System.out.println(head.printForward());
- System.out.println(isPalindrome2(head));
- }
-
- }