1. 程式人生 > >鏈表常見問題解析

鏈表常見問題解析

當前 新建 獲取 find 個數 時間 ins intersect null

找到鏈表的倒數第n個結點

方法1:

從鏈表的第一個結點開始,統計當前結點後面的結點個數。如果後面結點的個數小於n-1,那麽算法結束並返回消息"鏈表中的結點個數不足"。如果數量大於n-1,則移動到下一個結點。重復該過程直至當前結點後面的結點個數等於n-1。
[時間復雜度O(n^2),空間復雜度O(1)]

方法2:

該方法需要新建一個散列表,表結構是<結點的位置, 結點地址>。當遍歷鏈表時可以得到鏈表的長度記為M,求解這個問題只需要返回散列表中主鍵為M-n+1的值。
[時間復雜度O(M),空間復雜度O(M)]

方法3:

從表頭結點開始遍歷鏈表得到鏈表的長度M,然後在從表頭開始再一次遍歷得到第M-n+1個結點。
[時間復雜度O(M)+O(n)=O(n),空間復雜度O(1)]

方法4:

使用連個指針pNthNode和pTemp,首先兩個指針都指向鏈表的表頭結點。僅當pTemp沿著鏈表進行了n次移動後pNthNode才開始移動。然後兩個指針同時移動直至pTemp到達表尾,這時pNthNode所指的結點就是所求的結點。也就是鏈表的倒數第n個結點。
[時間復雜度O(n),空間復雜度O(1)]

ListNode nthNodeFromEnd(ListNode head, int nthNode){
    ListNode pTemp = head;
    ListNode pNthNode = null;
    for(int count=1; count<nthNode;count++){
        if(pTemp!=null){
            pTemp = pTemp.getNext();
        }
    }
    while (pTemp!=null){
        if(pNthNode == null){
            pNthNode = head;
        }else {
            pNthNode = pNthNode.getNext();
        }
        pTemp = pTemp.getNext();
    }
    if(pNthNode!=null){
        return pNthNode;
    }
    return null;
}

判定給定的鏈表是以NULL結尾,還是形成一個環

方法1:

從表頭結點開始,逐個遍歷鏈表中的每個結點。對於每個結點,檢查該結點的地址是否存在於散列表中。如果存在則表明當前範圍的結點已經被訪問過了。出現這種情況只能是因為給定的鏈表中存在環。如果散列表中沒有當前結點,那麽把該地址插入散列表中。
[時間復雜度O(n),空間復雜度O(n)]

方法2:

Floyd環判定算法。該方法使用兩個在鏈表中具有不同移動速度的指針。一旦他們進入環就會相遇。這個判定方法的準確性在於,快速移動指針和慢速移動指針將會指向同一位置的唯一可能情況就是整個或者部分鏈表時一個環。
[時間復雜度O(n),空間復雜度O(1)]

boolean doesLinedListContainsLoop(ListNode head){
    if(head == null){
        return false;
    }
    
    ListNode slowPtr = head;
    ListNode fastPtr = head;
    while (fastPtr.getNext()!=null && fastPtr.getNext().getNext()!=null){
        slowPtr = slowPtr.getNext();
        fastPtr = fastPtr.getNext().getNext();
        if(slowPtr == fastPtr){
            return true;
        }
    }
    return false;
}

判定給定的鏈表是以NULL結尾,還是形成一個環,如果鏈表中存在環,找到環的起始結點

Floyd環判定算法。
1.該方法使用兩個在鏈表中具有不同移動速度的指針。一旦他們進入環就會相遇。這個判定方法的準確性在於,快速移動指針和慢速移動指針將會指向同一位置的唯一可能情況就是整個或者部分鏈表時一個環。
2.當快速移動的指針和慢速移動的指針相遇後,讓慢速指針從表頭開始移動,而快速指針從相遇位置開始移動,快速指針和慢速指針每次都移動一個結點,當它們再次相的位置就是環的起始結點。
[時間復雜度O(n),空間復雜度O(1)]

ListNode findBeginOfLoop(ListNode head) {
    ListNode slowPtr = head;
    ListNode fastPtr = head;
    boolean loopExists = false;
    if (head == null) {
        return null;
    }
    while (fastPtr.getNext() != null && fastPtr.getNext().getNext() != null) {
        slowPtr = slowPtr.getNext();
        fastPtr = fastPtr.getNext().getNext();
        if (slowPtr == fastPtr) {
            loopExists = true;
            break;
        }
    }
    if (loopExists == true) { //環存在
        slowPtr = head;
        while (slowPtr != fastPtr) {
            fastPtr = fastPtr.getNext();
            slowPtr = slowPtr.getNext();
        }
        return slowPtr;
    }
    return null;
}

判定給定的鏈表是以NULL結尾,還是形成一個環,如果鏈表中存在環,返回環的長度

當快速指針和慢速指針相遇後,保持慢速指針不動,快速指針每次只移動一個結點,當他們再次相遇時,快速指針剛好比慢速指針都走了一個環的長度。
[時間復雜度O(n),空間復雜度O(1)]

int findLoopLength(ListNode head) {
    ListNode slowPtr = head;
    ListNode fastPtr = head;
    boolean loopExists = false;
    int counter = 0;
    if (head == null) {
        return 0;
    }
    while (fastPtr.getNext() != null && fastPtr.getNext().getNext() != null) {
        slowPtr = slowPtr.getNext();
        fastPtr = fastPtr.getNext().getNext();
        if (slowPtr == fastPtr) {
            loopExists = true;
            break;
        }
    }
    if (loopExists == true) {
        fastPtr = fastPtr.getNext();
        while (slowPtr != fastPtr) {
            fastPtr = fastPtr.getNext();
            counter++;
        }
    }
    return counter;
}

逆置單向鏈表

[時間復雜度O(n),空間復雜度O(1)]

ListNode reverseList(ListNode head) {
    ListNode temp = null;
    ListNode nextNode = null;
    while (head != null) {
        nextNode = head.getNext();
        head.setNext(temp);
        temp = head;
        head = nextNode;
    }
    return temp;
}

假設兩個單向鏈表在某個結點相交後,成為一個單向鏈表。兩個鏈表的表頭結點是已知的,但是相交的結點未知。並且兩個鏈表的結點數也可能不同。請設計算法找到兩個鏈表的合並點

獲取兩個鏈表的長度m,n.計算兩個長度的差d。從較長鏈表的表頭開始,移動d步。再兩個鏈表開始同時移動。直至出現兩個後繼指針值相等的情況。
[時間復雜度O(max(m,n),空間復雜度O(1)]

ListNode findIntersectingNode(ListNode list1, ListNode list2) {
    int len1 = 0;
    int len2 = 0;
    int diff = 0;
    ListNode head1 = list1;
    ListNode head2 = list2;
    while (head1 != null) {
        len1++;
        head1 = head1.getNext();
    }
    while (head2 != null) {
        len2++;
        head2 = head2.getNext();
    }
    if (len1 < len2) {
        head1 = list2;
        head2 = list1;
        diff = len2 - len1;
    } else {
        head1 = list1;
        head2 = list2;
        diff = len1 - len2;
    }
    for (int i = 0; i < diff; i++) {
        head1 = head1.getNext();
    }
    while (head1 != null && head2 != null) {
        if (head1 == head2) {
            return head1;
        }
        head1 = head1.getNext();
        head2 = head2.getNext();
    }
    return null;
}

檢查鏈表的長度是奇數還是偶數

使用一個在鏈表中每次向後移動兩個結點的指針。最後如果指針值為NULL。那麽鏈表長度為偶數,否則指針指向表尾結點,鏈表長度為奇數
[時間復雜度O(max(m,n),空間復雜度O(1)]

int isLinkedListLengthEven(ListNode head) {
    while (head != null && head.getNext() != null) {
        head = head.getNext().getNext();
    }
    if (head == null) {
        return 0;
    } else {
        return 1;
    }
}

鏈表常見問題解析