資料結構與演算法 | 迴文連結串列檢測

wangwei_hz發表於2019-01-31

pexels-photo-747964

原文連結:wangwei.one/posts/java-…

如何判斷一個單連結串列是否為迴文連結串列?

迴文連結串列

LeetCode 234. Palindrome Linked List

例1:

Input: 1->2
Output: false
複製程式碼

例2:

Input: 1->2->2->1
Output: true
複製程式碼

提升: 時間複雜度為O(n),空間複雜度為O(1).

解法一

直接將連結串列進行 反轉 ,然後將新的反轉連結串列與原連結串列進行比較,這種思路最為簡單粗暴。

此種解法的時間複雜度為O(n),空間複雜度為O(n).

程式碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public boolean isPalindrome(ListNode head) {
        if(head == null){
            return true;
        }
        ListNode newCurr = null;
        ListNode newPrev = null;
        ListNode curr = head;
        while(curr != null){
            newCurr = new ListNode(curr.val);
            newCurr.next = newPrev;
            newPrev = newCurr;
            curr = curr.next;
        }
        
        ListNode p1 = newCurr;
        ListNode p2 = head;
        
        while(p2 != null && p2 != null){
            if(p2.val != p1.val){
                return false;
            }
            p1 = p1.next;
            p2 = p2.next;
        }
        return true;
    }
}

複製程式碼

LeetCode效能測試:

Runtime: 3 ms, faster than 24.01% of Java online submissions forPalindrome Linked List.

解法二

為了降低空間複雜度到O(1),我們可以只對連結串列的後半部分直接反轉,然後將反轉後的後半部分與前半部分進行比較。

如何對後半部分進行反轉呢?這就涉及到我們前面的 如何找到中間節點 的方法,使用快慢指標,先找到中間節點,然後從中間節點開始反轉。

需要注意的是,在進行比較時,要以後半部分為基準進行遍歷來比較,例如在連結串列長度位偶數的情況下:

A -> B -> C -> C -> B -> A 反轉得到 A -> B -> C -> C <- B <- A,以前半部分為基準的話,會出現 null 指標的異常。

此種解法的時間複雜度為O(n),空間複雜度為O(1).

程式碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public boolean isPalindrome(ListNode head) {
        if(head == null){
            return true;
        }
        
        // 先找到中間節點,slow最後的結果就是中間節點
        ListNode slow = head;
        ListNode fast = head;
        
        for(ListNode curr = slow; slow != null; ){               
            if(fast == null || fast.next == null){
                break;
            }else{
                fast = fast.next.next;
            }
            slow = slow.next;
        }
        
        // 從slow開始,對後連結串列後半部分進行反轉
        ListNode prev = null;
        ListNode curr = slow;
        ListNode next = null;
        
        while(curr != null){
            next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        
        // 對前後兩個部分進行比較
        ListNode p1 = head;
        ListNode p2 = prev;
        
        while(p2 != null){
            if(p1.val != p2.val){
                return false;
            }
            p1 = p1.next;
            p2 = p2.next;
        }     
        return true;
    }
}
複製程式碼

LeetCode效能測試:

Runtime: 1 ms, faster than 93.05% of Java online submissions forPalindrome Linked List.

解法三

解法三比較巧妙,不容易想到。思路如下:

  • 定義左右兩個指標,左指標向有移動,"右指標向左移動",對比左右兩個指標是否配置。
  • 我們這裡是單連結串列,右指標怎麼向左移動呢?這裡通過遞迴的方式,當遞迴函式一層一層返回時,變相地實現了"右指標左移"的思路。

程式碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    
    private ListNode head;
    private ListNode left;
        
    public boolean isPalindrome(ListNode head1) {
        head = head1;
        return isPalindromeUtil(head1);
    }
    
    private boolean isPalindromeUtil(ListNode right){ 
        left = head; 
        
        // 當指向NULL時,停止遞迴
        if (right == null){
            return true; 
        } 
  
        // 向右移動指標,遞迴呼叫
        boolean isp = isPalindromeUtil(right.next); 
        if (isp == false){
            return false; 
        } 
  
        // 比較左右指標是否匹配
        boolean isp1 = (right.val == left.val); 

        // 移動左指標
        left = left.next; 
  
        return isp1; 
    } 
}
複製程式碼

LeetCode效能測試:

Runtime: 3 ms, faster than 22.70% of Java online submissions forPalindrome Linked List.

相關練習

參考資料

相關文章