双指针单向链表的快速复制发

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

相关文章
相关标签/搜索