#234. Palindrome Linked List

題目描述

給你一個鏈結串列的起點head,如果是一個回文鏈結串列回傳true,不是則回傳false

限制條件

  1. 節點數量為[1, 10^5]。
  2. 0 <= 節點值 <= 9

解題思路

Hash Table

由於我們只知道鏈結串列的起點,要知道所有的節點順序勢必要走完所有的節點,但因為無法直接指定某個位置的節點,所以要想辦法解決這個問題,這個時候就可以借助HashTable來記憶所有的節點順序,知道順序以後在前後比對就可以了。

public class Solution { 
	public bool IsPalindrome(ListNode head) {
        if(head.next==null) return true;
        var ht = new Hashtable();
        int count = 0;
        while(head!=null){
            ht.Add(count,head.val);
            head = head.next;
            count++;
        }
        int half = count/2;
        count--;
        for(int i=0;i<half;i++){
            if((int)ht[i]!=(int)ht[count]) return false;
            count--;
        }   
        return true;
    }
}

複雜度分析:

  • 時間複雜度:紀錄節點的順序時間為n,比對節點的時間為n/2,總花費時間為3/2n,因此時間複雜度為O(n)。
  • 空間複雜度:要儲存節點順序,因此空間複雜度為O(n)。

Follow-up

你可以讓執行時間為O(n)以及使用O(1)的空間嗎?

Divide and Reverse

若要讓空間複雜度為O(1),就不能使用Hash Table來解決這一題,所以我們要換個方式。
首先先確定整個鏈結串列的長度,得到中間值,接著從中間的節點開始把鏈結反向,然後就會得到從尾端開始的鏈結串列,只要比對從頭開始和從尾端開始的值,就可以知道鏈結串列是否為回文結構了。

public class Solution { 
	public bool IsPalindrome(ListNode head) {        
        if(head==null ||head.next==null) return true;
        var temp = head;
        var count = 0;
        while(temp!=null){
            temp = temp.next;
            count++;
        }
        temp = head;
        count/=2;
        for(int i=0;i<count;i++){
            temp = temp.next;
        }                
        var tail = Reverse(temp);        
        if(tail==null) return false;
        while(head!=null && tail!=null){            
            if(head.val != tail.val) return false;
            head = head.next;
            tail = tail.next;
        }
        return true;
    }
    public ListNode Reverse(ListNode head){
        if(head==null ||head.next==null) return head;
        var prev = head;
        var curr = head.next;
        head.next = null;
        while(curr.next!=null){
            var next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        curr.next = prev;
        return curr;
    }
}

複雜度分析:

  • 時間複雜度:找出長度的時間為n,定位中間節點的時間為n/2,反轉節點為n/2,比對節點為n/2,總時間為5/2n,因此時間複雜度為O(n)。
  • 空間複雜度:空間複雜度為O(1)。

Two Pointers

雖然上面的解法可以解決掉問題,但是步驟太繁瑣了,可以做出一些調整來改善。 首先可以改成利用雙指標法,快指標的速度是慢指標的兩倍,所以當快指標到達終點時,慢指標剛好走到中間。 然而這邊仍然要把節點反向才能夠去比對節點,但是可以不用等到找到中間點才開始做,只要慢指標開始移動時,邊移動邊把當下的節點做反向即可。 並且當慢指標走道中間時,就可以馬上進行比對了。

public class Solution { 
	public bool IsPalindrome(ListNode head) {
        if(head==null || head.next==null) return true;
        ListNode slow = head;
        ListNode fast = head;
        ListNode prev = null;        
        while(fast!=null && fast.next!=null){            
            fast = fast.next.next;
            ListNode tmp = slow.next;
            slow.next = prev;
            prev = slow;
            slow = tmp;
        }        
        if(fast!=null) slow = slow.next;
        while(slow!=null){
            if(slow.val!=prev.val) return false;
            slow = slow.next;
            prev = prev.next;
        }
        return true;
    }
}

複雜度分析:

  • 時間複雜度:快慢指標找到中間節點位置時,只花了n/2的時間,而從中間開始比對也只花了n/2的時間,所以總共花費了n的時間,因此時間複雜度為O(n)。
  • 空間複雜度:空間複雜度為O(1)。

學習重點

  • 雜湊表(Hash Table)。
  • 雙指標(Two Pointers)。
Last Updated:
Contributors: eisen