zoukankan      html  css  js  c++  java
  • 剑指offer面试题26-复杂链表的复制

    题目:

    请实现函数ComplexListNode* Clone(ComplexListNode* pHead)。复制一个复杂链表。

    在复杂链表中。每个节点除了一个m_pNext指针指向下一个节点外,另一个m_pSibling指向链表中的随意节点或者NULL,节点的定义例如以下:


    package com.aii.algorithm;
    
    public class Node {
    	int value;
    	Node next;
    	Node sibling;
    
    	public Node(int value) {
    		this.value = value;
    	}
    
    
    	public Node() {
    	}
    
    }
    




    一.思路:

    能够分两步来实现:

    1.先复制链表。sibling就用旧链表的,照搬。

    2.再复制silbing

    怎么复制sibling,这个借助一个HashMap(在O(1)时间内可以找到),将Node与其复制的Node'的映射关系存到HashMap中。

    在第二步改动sibling的时候,依据原先sibling的Node去Map中找,找到相应的那个就是他应该指向的那个。


    代码实现:

    package com.aii.algorithm;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class CloneNodes {
    	// 第一步:复制链表
    	// 第二步:复制sibing的指针位置
    	public Node clone(Node head) {
    		Map<Node, Node> map = new HashMap<Node, Node>();
    
    		// copy linkedlist
    		Node newHead = cloneNode(head, map);
    
    		// copy silbing points
    		Node tmpNewHead = newHead;
    		while (tmpNewHead != null) {
    			// get from mapping
    			tmpNewHead.sibling = map.get(tmpNewHead.sibling);
    			tmpNewHead = tmpNewHead.next;
    		}
    		return newHead;
    	}
    
    	// 1.递归克隆
    	// 2.同一时候将Node,以及Node'的映射关系放入到map中,方便map.next查找
    	private Node cloneNode(Node head, Map<Node, Node> map) {
    		if (head == null) {
    			return null;
    		}
    		Node node = new Node();
    		node.next = cloneNode(head.next, map);
    		node.sibling = head.sibling;
    		map.put(head, node);
    		node.value = head.value;
    		return node;
    	}
    }
    



    二、

    这里借助了HashMap

    假设不借助HashMap,又要在O(n)时间完毕?


    不使用Map,在改动sibling的时候。麻烦之处在哪?

    在于找不到新复制的节点。

    下图:




    比方说要在A'位置找A'的silbing,那就要A'与A一起往前走,直到某个节点Node==A.sibling这个时候相应的Node'也应该是A'.silbing。

    可是这种时间复杂度为O(n*n)


    这里有一个好的办法:

    分三步走:

    1.将新复制的链表穿插到旧的链表中


    2.这样子就行依据左右位置的旧的节点找到sibling位置了


    3.还原。



    代码实现:

    package com.aii.algorithm;
    
    public class CloneNodes2 {
    
    	// 1.将新的节点插入到链表原始节点的后面
    	// 2.将新的节点的sibling的指针给赋值。是原先值的next
    	// 3.再将新的节点链表从中独立出来
    	public Node clone(Node head) {
    		Node tmp = head;
    		// 1.第一步。插入新的节点
    		while (tmp != null) {
    			Node next = tmp.next;
    			Node node = new Node();
    			node.value = tmp.value;
    			node.next = tmp.next;
    			tmp.next = node;
    			//
    			tmp = next;
    		}
    
    		// 2.第二步,设置sibling
    		int count = 0;
    		tmp = head;
    		Node nodeNext = null;
    		while (tmp != null) {
    			if (count++ % 2 == 0) {
    				nodeNext = tmp.next;
    			} else {
    				tmp.next = nodeNext.next;
    			}
    			tmp = tmp.next;
    		}
    
    		// 3.第三步。将新的链表独立出来,而且还原原先的链表
    		tmp = head;
    		Node newHead = null;
    		count = 0;
    		while (tmp != null) {
    			tmp.next = tmp.next.next;
    			newHead = tmp.next;
    
    			tmp = tmp.next;
    		}
    
    		return newHead;
    	}
    }
    


    三、測试用例:

    因为引用关系比較复杂,toStringeasy引发空指针。这里附上測试用例:

    package com.aii.algorithm;
    
    import org.junit.Before;
    import org.junit.Test;
    
    public class CloneNodes2Test {
    	private Node head = new Node('a');
    
    	@Before
    	public void init() {
    		Node n2 = new Node('b');
    		Node n3 = new Node('c');
    		Node n4 = new Node('d');
    		Node n5 = new Node('e');
    
    		head.next = n2;
    		n2.next = n3;
    		n3.next = n4;
    		n4.next = n5;
    
    		//
    		head.sibling = n3;
    		n2.sibling = n5;
    		n3.sibling = n2;
    	}
    
    	@Test
    	public void test() {
    		printNode(head);
    		Node node = new CloneNodes().clone(head);
    		System.out.println("-----");
    		printNode(node);
    		System.out.println("-----");
    		printNode(head);
    	}
    
    	private void printNode(Node node) {
    		Node tmp = node;
    		while (tmp != null) {
    			int sibling = -1;
    			if (tmp.sibling != null) {
    				sibling = tmp.sibling.value;
    			}
    			int next = -1;
    			if (tmp.next != null) {
    				next = tmp.next.value;
    			}
    			System.out.print("value:" + (char) tmp.value);
    			System.out.print("	sibling:" + (char) sibling);
    			System.out.println("	next:" + (char) next);
    			tmp = tmp.next;
    		}
    	}
    }
    

    结果:

    value:a sibling:cnext:b
    value:b sibling:enext:c
    value:c sibling:bnext:d
    value:d sibling:?next:e
    value:e sibling:?next:?


    -----
    value:a sibling:cnext:b
    value:b sibling:enext:c
    value:c sibling:bnext:d
    value:d sibling:?next:e
    value:e sibling:?next:?
    -----
    value:a sibling:cnext:b
    value:b sibling:enext:c
    value:c sibling:bnext:d
    value:d sibling:?next:e
    value:e sibling:?

    next:?




  • 相关阅读:
    八数码
    狂神说笔记——多线程05
    狂神说笔记——Java SE基础03
    从零开始的卷积神经网络
    深度学习与机器学习的区别
    模型的评估与选择
    经验风险VS风险函数
    常见的损失函数
    Erlang聊天室
    uniapp APP端 清除缓存
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7072660.html
Copyright © 2011-2022 走看看