1. 程式人生 > >【leetcode】leetcode 刷題 筆記 (不定期更新)

【leetcode】leetcode 刷題 筆記 (不定期更新)

237.Delete Node in a Linked List

題目要求只給定連結串列中一個節點的前提下,刪除該節點。注意這裡題目並沒有給出對連結串列的引用,因而我們無法遍歷連結串列得到前驅。有一個思路確實很好,它並不是刪除連結串列的節點,而是把該節點的內容改為下一個節點的內容,把該節點的指標給為下一個節點的指標,相當於複製了下一個節點的內容然後刪除下一個節點。

思考:這個思路在只有單鏈表的前提下確實無懈可擊,不過不但這個連結串列被運用到實際應用中會有問題產生,因為應用中的例項可能會持有連結串列節點的引用,這種刪除方式會導致引用發生錯誤,比如原本持有3的引用,正常來看,一旦刪除3,就應該持有null才對,但是上述做法卻讓其持有了4的引用,反而原本持有4的引用的物件持有了null的引用,這是一個弊端。

234.Palindrome Linked List

Palindrome意思是迴文,這個題目給出了判定迴文的思路,即同時從前和從後遍歷,比較兩端的元素,只要有一次不同,就返回false,迴圈結束返回true。

(1)   這裡還涉及如何反轉一個連結串列,要設定next和pre指標,當遍歷到p時,要改變它的next為pre,但是這樣會丟掉原本連結串列的順序,因此要用next先記錄p的next,最後設定pre為p,p為之前儲存的next

(2)   如何找到連結串列的中間元素?設定slow和fast兩個指標,每一次迭代,slow設為下一個,fast設為下兩個。直到fast為null或者fast.next為null為止。

按照上述解法得到的右側鏈表含有較多的元素(奇數情況),然後把右側反轉後,右側含有較少的元素(偶數情況,奇數反而相同,因為中間的元素被兩邊共享),因此,在最後雙向遍歷時,應當使用右側鏈表的頭指標為條件。畫個圖就很清楚了。

public class ListNode {

	private ListNode next;
	private int data;

	public ListNode(int data){
		this.data = data;
	}
}


public class Solution {

	public boolean isPalindrome(ListNode head){
		ListNode slow = head;
		ListNode fast = head;
		while(fast != null && fast.getNext() != null){
			slow = slow.getNext();
			fast = fast.getNext().getNext();
		}
		//右側更少
		ListNode back = reverse(slow);
		while(back != null){
			if (back.getData() != head.getData()) {
				return false;
			}
			back = back.getNext();
			head = head.getNext();
		}
		return true;
	}
	
	public ListNode reverse(ListNode head){
		ListNode p = head;
		ListNode pre = null, next = null;
		while(p != null){
			next = p.getNext();
			p.setNext(pre);
			pre = p;
			p = next;
		}
		return pre;
	}
}

203.Remove Linked List Elements

連結串列中的刪除操作,首先很重要一點是如果要刪除頭結點,我們在函式中是無法實現的,因為函式引數中的head是函式棧中對於呼叫處的真正head引用的拷貝,我們在函式中改變對head的賦值,並不能改變呼叫處head的賦值,唯一的解決辦法就是讓函式返回一個新的head,然後呼叫結束之後更新head。

思路:因為刪除操作在單鏈表中必須得到前一個元素,因此遍歷時只能檢查下一個元素的值是否相等。While迴圈的條件是下一個元素是否為空。如果相等,修改next引用,否則,遍歷繼續。

這樣的實現有一點需要注意,就是頭結點無法檢測,因為我們總是檢測下一個元素是否相等,因此在上述迴圈之前,我們必須先對頭節點檢測,只要head相等,那麼head後移。然後再進行上面的while迴圈,這樣上面的while迴圈條件不僅要有itr.next!=null還要有itr!=null,因為對head檢測結束,head可能為null。

public ListNode remove(ListNode head, int value){
		while(head != null && head.getData() == value){
			head = head.getNext();
		}
		ListNode p = head;
		while(p != null && p.getNext() != null){
			if (p.getNext().getData() == value) {
				p.setNext(p.getNext().getNext());
			}else {
				p = p.getNext();
			}
		}
		return head;
	}


那麼為什麼同樣是檢測下一個是否等於value,head處於後面的處理必須分開?因為head是對引用賦值,而後面是對引用指向的元素的屬性賦值。因此在while迴圈內部的處理邏輯完全不同。即要修改連結串列元素的next域時,head和後面的操作不同。Head是要後移,而後面的元素是賦值。

這裡還有一種思路,就是新建一個dummy節點,其資料域一定不等於value,比如value+1。然後再進行上面的while迴圈,這樣做的好處就是省去了頭結點的處理,用空間換時間。使得while迴圈 內部的處理邏輯相同。

	public ListNode remove1(ListNode head, int value){
		ListNode dummy = new ListNode(value + 1);
		dummy.setNext(head);
		ListNode p = dummy;
		while(p != null && p.getNext() != null){
			if (p.getNext().getData() == value) {
				p.setNext(p.getNext().getNext());
			}else {
				p = p.getNext();
			}
		}
		return dummy.getNext();
	}

160.Intersection of Two Linked Lists

這道題目有一個要求,那就是intersecting的一個或者多個元素之後,兩個連結串列的長度必須相同。否則下述演算法達不到效果。在上述前提下,這個演算法在找相同元素的時候只用了On的複雜度,正常情況下我們需要On2的複雜度。如果存在,那麼返回非空,否則,當a==b時,均為null,會返回null。

	public ListNode firstIntersect(ListNode headA, ListNode headB){
		ListNode a = headA;
		ListNode b = headB;
		
		while(a != b){
			a = a == null?headB:a.getNext();
			b = b == null?headA:b.getNext();
		}
		return a;
	}

141.Linked List Cycle

這是快慢指標在連結串列中的兩一個應用,除了找中間元素意外,還可判斷是否有環路。Fast指標的條件是非空和下一個非空,那麼結束時,要麼null要麼最後一個。

	public boolean hasCycle(ListNode head){
		ListNode slow = head;
		ListNode fast = head;
		while(fast !=null && fast.getNext() != null){
			slow = slow.getNext();
			fast = fast.getNext().getNext();
			if (slow == fast) {
				return true;
			}
		}
		return false;
	}

83.Remove Duplicates from Sorted List

234已經涉及。比較簡單,主要是一個while迴圈,然後迴圈內部比較當前與下一個,因此,最後一個元素肯定在前驅就會被處理,所以迴圈條件可以是next與當前均不為null。迴圈體內部處理就是一個if,如果不等,就迭代至下一個,反之,進行next拷貝。

不過這個題目給出了一個刪除連結串列中重複元素的方法,就是先排序,然後按照上面方法,複雜度是nlogn。

注意上述方法的前提是排序。

public ListNode deleteDuplicates(ListNode head) {
		ListNode p = head;
        while(p != null && p.getNext() != null){
        	if (p.getData() != p.getNext().getData()) {
				p = p.getNext();
			}else {
				p.setNext(p.getNext().getNext());
			}
        }
        return head;
    }

24.Swap Nodes in Pairs

首先把head指標調成第二個元素,然後是一個while迴圈,不斷調整相鄰的兩個元素。這裡還必須設定一個pre引用,因為while內的處理邏輯會用到之前一次迭代的元素,這個與reverse操作比較類似,也是一個注意點,就是說只要需要用到之前一次迭代的元素,那麼就用一個pre來記錄。

public ListNode swapPairs(ListNode head) {
		ListNode p = head;
	    if (head != null && head.getNext() != null) {
			head = head.getNext();
		}
	    ListNode pre = null;
	    while(p != null && p.getNext() != null){
	    	ListNode p1 = p.getNext();
	    	p.setNext(p1.getNext());
	    	p1.setNext(p);
	    	if (pre != null) {
	    		pre.setNext(p1);
			}
	    	pre = p;
	    	p = p.getNext();
	    }
	    return head;
	}