剑指offer(Java)-复杂链表的复制

题目描述:

复制一个复杂链表,在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个sbiling指向链表中的任意结点或者null。

下图是一个复杂链表的示例,Null的指针没有画出。
这里写图片描述

解题思路:

1.很直观的解法就是分成两步:

1).复制原始链表上的每一个结点,并用next指针连起来。
2).复制sbiling指针。
但是复制sbiling指针时需要比较高的复杂度。
以上图为例,如果我们要复制B对应B’的的sbiling指针,那就是要找到E’,想要找到E’只能根据
B到E要走的步数 = B’到E’要走的步数
然而又如D.sbiling = B,指向的结点在它的前面,而链表并没有指向前一个元素的指针,所以,每次都只能根据从链表头结点到目标的结点的步数来找到sbiling应该指向的元素
这种方法显然效率太低,时间复杂度达到了O(n*n)。

2.书中提到了利用哈希表存储(N, N’)的配对信息的方法

这是一个在时间上很高效的方法,在查找上,利用哈希表的高效性。但是缺点在于要用额外的空间。

上述方法的时间主要花在定位结点的m_pSibling上面,我们试着在这方面做优化。还是分两步:第一步仍然是复制原始链表上的每个结点N,并创建 N’,然后把这些创建出来的结点链接起来。同时我们把<N, N’>的配对信息放到一个哈希表中。第二步还是设置复制后的链表上每个结点的m_pSibling。如果在原始链表中结点N的m_pSibling 指向结点S,那么在复制后的链表中,对应的N’应该指向S’。由于有了哈希表,可以用O(1)的时间根据S找到S’。这种方法相当于用空间换时间,以 O(n)的空间消耗把时间复杂度由O(n^2)降低到O(n)。

(注:哈希表中的每一个配对(pair)的Key是原始链表的结点,Value是Key中 结点的对应的拷贝结点。这样在哈希表中,就可以在O(1)的时间里,找到原始结点对应的拷贝出来的结点。比如想求得N’的m_pSibling所指向的 S’,可以由N的m_pSibling求得S,而<S, S’>的配对信息就在哈希表中,可以用O(1)时间求得。)

3.更为高效的一种不利用辅助空间的方法

这个方法的巧妙之处在于利用链表结点本身记录sbiling指针的位置。
分成三个步骤
1).根据原始链表的每个结点N创建对应的N’,并把N’连在N的后面。
如下图:
这里写图片描述

2)看到上图我们就应该知道这个算法的巧妙之处了,B’.sbiling就记录在B.sbiling.next,这一步就是通过这个方法设置sbiling指针了。

3).将两个链表断开。

 

package cglib;


class ComplexListNode
{
int data;
ComplexListNode next;
ComplexListNode sibling;

@Override
public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("data = " + data);
    sb.append(", next = " + (next == null ? "null" : next.data));
    sb.append(", sbiling = " + (sibling == null ? "null" : sibling.data));
    return sb.toString();
}
}


public class DeleteNode{
    

        public static void copyList(ComplexListNode head){

            ComplexListNode node = head;
            while(node != null){

                ComplexListNode copyNode = new ComplexListNode();
                copyNode.data = node.data;//1的数据给01
                copyNode.next = node.next;//1指向2的指针给01,相当于01指向2
                copyNode.sibling = null;
                 //1->2->3,变成1->01->2->02->3->03
                node.next = copyNode;//1指向01
                node = copyNode.next;//然后以2开始又继续下一个while循环
            }


        }
          //1->01->2->02->3->03,1--->3,01--->03
        public static void setSbiling(ComplexListNode head){
            ComplexListNode node = head;

// 当前处理的结点sibling字段不为空,则要设置其复制结点的sibling字段


            while(node != null){
                ComplexListNode copyNode = node.next;//01开始
                if(node.sibling != null){//1指向3
                    copyNode.sibling = node.sibling.next;//01指向03,相当于1指向3,3的下一个结点就是03
                }
                node = copyNode.next;
            }
        }
      //1->01->2->02->3->03,1--->3,01--->03
        public static ComplexListNode disConnectList(ComplexListNode head){
            ComplexListNode node = head;
            ComplexListNode copyHead = null;
            ComplexListNode copyNode = null;

            if(node != null){// node 不为空才能进行下面的while循环
                copyHead = node.next;//01为头
                copyNode = node.next;
                node.next = copyNode.next;//1的下一个01变成01的下一个2
                node = node.next;//然后将2作为下一个结点进入下面的while循环
            }

            while(node != null){

                copyNode.next = node.next;//01的下一个就是02, //把偶数位置的结点链接起来就是复制出来的新链表
                copyNode = copyNode.next;//以02开始进行下一个, //把奇数位置的结点链接起来就是原始链表  

                node.next = copyNode.next;//2的下一个就是3,相当于02的下一个
                node = node.next;//以3作为结点进行下一个
            }

            return copyHead;
        }

        public static ComplexListNode copy(ComplexListNode head){
            copyList(head);
            setSbiling(head);
            return disConnectList(head);
        }

        public static void main(String[] args) {

            ComplexListNode head = new ComplexListNode();
            head.data = 1;

            ComplexListNode node2 = new ComplexListNode();
            node2.data = 2;

            ComplexListNode node3 = new ComplexListNode();
            node3.data = 3;

            ComplexListNode node4 = new ComplexListNode();
            node4.data = 4;

            ComplexListNode node5 = new ComplexListNode();
            node5.data = 5;

            head.next = node2;
            head.sibling = node3;

            node2.next = node3;
            node2.sibling = node5;

            node3.next = node4;

            node4.next = node5;
            node4.sibling = node2;

            ComplexListNode copyHead = copy(head);

            ComplexListNode node = copyHead;
            while(node != null){
                System.out.println(node);
                node = node.next;
            }

        }
}


输出:

data = 1, next = 2, sbiling = 3
data = 2, next = 3, sbiling = 5
data = 3, next = 4, sbiling = null
data = 4, next = 5, sbiling = 2
data = 5, next = null, sbiling = null

 

下面是map实现:

 

package cglib;

import java.util.HashMap;

class RandomListNode
{

int data;

RandomListNode next = null;

RandomListNode sibling = null;

 

RandomListNode(int data) {

    this.data = data;

}

@Override
public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("data = " + data);
    sb.append(", next = " + (next == null ? "null" : next.data));
    sb.append(", sbiling = " + (sibling == null ? "null" : sibling.data));
    return sb.toString();
}
}


public class DeleteNode{
    

     public static RandomListNode Clone(RandomListNode pHead)
    
        {
    
            if(pHead == null)
                return null;
            

            HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
    
            RandomListNode newHead = new RandomListNode(pHead.data);
    
            RandomListNode pre = pHead, newPre = newHead;//pre用于扫描原链表
          //newHead指向复制链表,newPre用于扫描复制链表
            map.put(pre, newPre);
    
            while(pre.next != null){
    
                newPre.next = new RandomListNode(pre.next.data);
    
                pre = pre.next;
    
                newPre = newPre.next;
    
                map.put(pre, newPre);
    
            }
    
            pre = pHead;
    
            newPre = newHead;
    
            while(newPre != null){
    
                newPre.sibling = map.get(pre.sibling);
    
                pre = pre.next;
    
                newPre = newPre.next;
    
            }
    
            return newHead;
    
        
        }
    
     public static void main(String[] args) {

        RandomListNode head = new RandomListNode(1);
        

        RandomListNode node2 = new RandomListNode(2);
         

         RandomListNode node3 = new RandomListNode(3);
        

         RandomListNode node4 = new RandomListNode(4);
        

         RandomListNode node5 = new RandomListNode(5);
         

         head.next = node2;
         head.sibling = node3;

         node2.next = node3;
         node2.sibling = node5;

         node3.next = node4;

         node4.next = node5;
         node4.sibling = node2;

         RandomListNode copyHead = Clone(head);

         

         RandomListNode node = copyHead;
         while(node != null){
             System.out.println(node);
             node = node.next;
         }
         

     }
}


输出:

data = 1, next = 2, sbiling = 3
data = 2, next = 3, sbiling = 5
data = 3, next = 4, sbiling = null
data = 4, next = 5, sbiling = 2
data = 5, next = null, sbiling = null

 

转载于:https://my.oschina.net/u/2822116/blog/718441

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值