1. 程式人生 > >劍指offer:連結串列中倒數第k個節點

劍指offer:連結串列中倒數第k個節點

題目描述

輸入一個連結串列,輸出該連結串列中倒數第k個結點。

首先想到的是從頭結點開始遍歷到連結串列的末尾,然後往前回溯k個節點,但是回溯?  這種方式只適用於雙向連結串列,對於單向連結串列,是不可行的。

另外,如果我們已知了連結串列的長度,為n,那麼倒數第k個節點,也就是從前往後的第n-k+1個節點,我們從前往後遍歷n-k+1即可,然而我們並不知道連結串列的長度,所以需要先遍歷一遍連結串列,才能取得它的長度,再遍歷一邊連結串列,才能輸出第k個節點。所以這種方法的時間複雜度較高,需要遍歷兩遍才可以輸出。

實現1:

public ListNode FindKthToTail(ListNode head,int k) {
        ListNode p=head;
        if(head==null||k==0){
            return null;
        }
        int length=1;
        while(head.next!=null){
            head=head.next;
            length++;
        }
        if(k>length){
            return null;
        }
        for (int i = 1; i <length-k+1; i++) {
            if(p.next!=null){
                p=p.next;
            }
            else{
                return null;   
            }  
        }
        return p;
 
    }

實現2:用堆疊的方式去實現

 public ListNode FindKthToTail(ListNode head,int k) {
        ListNode result=null;
		if(head==null||k==0){
			return null;
		}
		Stack<ListNode> s=new Stack<>();
		while(head!=null){
			s.push(head);
			head=head.next;
		}
		for (int i =1; i <= k; i++) {
			if(!s.isEmpty()){
				result=s.pop();
			}else{
				result=null;
			}			
		}
		return result;
    }

上面的這兩種實現時間複雜度太高,並不是我們期望的結果。

如果想要只遍歷一遍連結串列,就輸出倒數的第k個元素,那麼我們就需要兩個指標。

(1)開始,讓指標p1,p2都指向頭結點,指標2先不動,指標1向前走k-1步;此時p1和p2之間相差k-1;

(2)從第k步開始,p1和p2同時移動,當p1指向連結串列的末尾時,此時p1所指的元素就是倒數第k個元素。

初步實現:

public ListNode FindKthToTail2(ListNode head,int k) {         ListNode p1=head;         ListNode p2=head;         //第一個指標先走,直到遍歷到連結串列的第k-1個節點         for (int i = 1; i <=k-1; i++) {             p1=p1.next;         }         //此時p1和p2之間相差k-1個元素,現在兩個指標同時遍歷         while(p1.next!=null){             p1=p1.next;             p2=p2.next;         }         return p2;

    }

這個實現是有問題的,會報空指標異常,因為程式碼中存在多種特殊的情況沒有被考慮到。

首先,如果給了一個空連結串列,那它的next是不存在的,此時會報錯,

其次,如果給的k=0,則k-1=-1,也會報錯;

另外,需要返回的k大於所給的連結串列的總長度時,也會報錯,

考慮到以上情況,對程式碼做一些安全性的考慮,實現如下:

實現3:

 public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null||k==0){
            return null;
        }
        ListNode p1=head;
        ListNode p2=head;
        //第一個指標先走,直到遍歷到連結串列的第k-1個節點
        for (int i = 1; i <=k-1; i++) {
            if(p1.next!=null){
                p1=p1.next;
            }
            else{
                return null;
            }
        }
        //此時p1和p2之間相差k-1個元素,現在兩個指標同時遍歷
        while(p1.next!=null){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
 

參考: