雙指針單向鏈表的快速複製發

更詳細的講解和代碼調試演示過程,請參看視頻
如何進入google,算法面試技能全面提高指南java

有一種較爲特殊的單向鏈表,以下:
node

這種鏈表一個特色是,除了next指向下一個節點外,它還多了一個指針jump,這個指針指向隊列中的某一個節點,這個節點能夠是當前節點本身,也能夠是隊列中的其餘節點。例如上圖,節點0的jump指針指向了最後一個節點,而節點1的jump指針指向了它本身。這種鏈表有一個專門的名稱,叫Posting List.面試

要求,你設計一個算法,複製給定的一個Posting List。算法的時間複雜度是O(n), 算法除了運行分配賦值節點所需的內存外,不能分配多餘內存,例如給定上面隊列,你的算法智能多分配五個節點的內存。算法能夠更改原隊列,當更改後,須要將隊列恢復原狀。算法

這道題目有必定難度,難點在於如何複製jump指針。若是沒有jump指針,那麼複製一個單項鍊表是很容易的。咱們先設想一個最簡單的作法,先不考慮jump節點,先把單項隊列複製出來,而後再考慮如何設置新隊列節點的jump指針。微信

一個作法是,給定一個具體的節點,而後將隊列遍歷以便,判斷jump節點與當前節點的距離,例如給定節點0,咱們經過遍歷得知,jump指向的節點,與當前節點有四個節點的距離,而後在新複製的隊列中,重新賦值的節點0日後走4個節點,找到新拷貝的節點4,接着把新節點0的jump指針指向新生成的節點4.dom

上面作法有個問題就是,設置每一個節點的jump指針時,都得將隊列遍歷一次,這樣的話,整個算法複雜度會是 O(n^2).但題目要求,算法複雜度必須是O(n),由此,咱們必須從新思考新的算法。機器學習

個人作法是這樣的,首先遍歷隊列,爲每一個節點生成一個拷貝:函數

接着,把原節點的next指針指向對應的拷貝節點,拷貝節點的next指針指向原節點原來next指向的節點:post


通過上面的變更,原節點跟它本身的拷貝鏈接了起來,同時原隊列的鏈接性任然得以保持,例如圖中,上面的節點0要抵達它的下一個節點,那麼只須要經過next指針到達它的拷貝節點,也就是下面的節點0,而後再經過拷貝節點的next指針就能夠抵達上面的節點1了。學習

此時,新節點的jump指針就容易設置了,例如要設置新拷貝的節點0的jump指針,先經過它原節點的jump指針,找到節點4,而後再經過節點4的next指針,找到節點4的拷貝節點,也就是上圖下方的節點4,最後把拷貝節點0的 jump指針設置成對應的上圖下方的節點4便可。若是用node來表示原節點,cpNode來表示對應的拷貝節點,下面代碼就能夠設置拷貝節點的jump指針:

cpNode = node.next; //得到拷貝節點
cpNode.jump = node.jump.next;

遍歷原隊列的每一個節點,採起上面的操做,這樣,新拷貝節點的jump指針就可以正確的設置了。

最後,恢復原隊列以及設置拷貝節點的next指針。因爲拷貝節點的next指針,指向原節點原來next指向的對象,由此只要把原節點的next設置爲拷貝節點的next指向的對象,就能夠復原原來的狀態。拷貝節點0的next要想指向拷貝節點1,首先經過它本身的next,找到原節點1,也就是圖中上方的節點1,而後經過原節點1的next找到對應的拷貝節點,也就是圖中下方的節點1,因而把拷貝節點0的next指針就能夠指向拷貝節點1,從而實現圖中下方的節點0經過next指針指向拷貝節點1。實現代碼以下:

cpNode = node.next; //得到拷貝節點
node.next = cpNode.next; //恢復原節點的next指針
node = node.next; 
cpNode.next = node.next;  //將當前拷貝節點的next指針指向下一個拷貝節點。

上面算法,每一步驟的時間複雜度都是o(n),同時咱們只爲新節點分配內存,除此只爲,並無多餘的內存分配,所以,算法符合題目要求。咱們看看具體的代碼實現:

public class ListUtility {
    private Node tail;
    private Node head;
    private int listLen = 0;
    private int postingListLen = 0;
    HashMap<Integer, PostingNode> map = new HashMap<Integer, PostingNode>();

    PostingNode createPostingList(int nodeNum) {
        if (nodeNum <= 0) {
            return null;
        }

        postingListLen = nodeNum;
        PostingNode postingHead = null, postingTail = null;
        PostingNode postingNode = null;
        int val = 0;
        while (nodeNum > 0) {
            if (postingNode == null) {
                postingHead = new PostingNode();
                postingHead.val = val;
                postingNode = postingHead;
                postingTail = postingHead;

            } else {
                postingNode.next = new PostingNode();
                postingNode = postingNode.next;
                postingNode.val = val;
                postingNode.next = null;
                postingTail = postingNode;
            }

            map.put(val, postingNode);
            val++;
            nodeNum--;
        }

       PostingNode tempHead = postingHead;
       createJumpNode(tempHead);

        return postingHead;
    }

    private void createJumpNode(PostingNode pHead) {
        Random ra = new Random();

        while (pHead != null) {
            int n = ra.nextInt(postingListLen);
            pHead.jump = map.get(n);
            pHead = pHead.next;
        }
    }

    public void printPostingList(PostingNode pHead) {
        while (pHead != null) {
            System.out.print("(node val:" + pHead.val + " jump val : " + pHead.jump.val + " ) ->");
            pHead = pHead.next;
        }

        System.out.print(" null ");
    }

    ....
 }

上面的代碼用於建立一個Posting list, 鏈表的複雜算法實如今類PostingList.java中,代碼以下:

public class PostingList {
    private PostingNode head ;
    private PostingNode copyHead;

    public PostingList(PostingNode node) {
        this.head = node;
    }

    public PostingNode copyPostingList() {
        createPostingNodes();
        createJumpNodes();
        ajustNextPointer();

        return copyHead;
    }

    private void createPostingNodes() {
        PostingNode node = null;
        PostingNode tempHead = head;
        while (tempHead != null) {
            node = new PostingNode();
            node.next = tempHead.next;
            node.val = tempHead.val;
            tempHead.next = node;
            tempHead = node.next;

        }
    }

    private void createJumpNodes() {
        PostingNode temp = head;
        copyHead = temp.next;

        while (temp != null) {
            PostingNode cpNode = temp.next;
            cpNode.jump = temp.jump.next;
            temp = cpNode.next;
        }
    }


    private void ajustNextPointer() {
        PostingNode temp = head;
        while (temp != null) {
            PostingNode cpNode = temp.next;
            temp.next = cpNode.next;
            temp = temp.next;
            if (temp != null) {
                cpNode.next = temp.next;    
            } else {
                cpNode.next = null;
            }

        }
    }
 }

createPostingNodes 執行的是步驟1,它爲每個節點生成一個拷貝。
createJumpNodes 執行步驟2,它爲每個拷貝節點設置他們對應的jump指針
ajustNextPointer 執行步驟3,它調整原隊列節點的next指針,以及設置拷貝節點的next指針。

每一個函數裏,只包含一個while循環,用於遍歷隊列,所以算法實現的複雜度是o(n).具體的代碼講解和調試演示過程,請參看視頻。

更多技術信息,包括操做系統,編譯器,面試算法,機器學習,人工智能,請關照個人公衆號:

本文分享自微信公衆號 - Coding迪斯尼(gh_c9f933e7765d)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索