更详细的讲解和代码调试演示过程,请参看视频
如何进入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源创计划”,欢迎正在阅读的你也加入,一块儿分享。