更詳細的講解和代碼調試演示過程,請參看視頻
如何進入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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。