您的位置:首页 > 职场人生

剑指offer面试题26-复杂链表的复制

2015-08-24 23:03 716 查看
题目:

请实现函数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;
	}
}


三、测试用例:

由于引用关系比较复杂,toString容易引发空指针,这里附上测试用例:

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("\tsibling:" + (char) sibling);
			System.out.println("\tnext:" + (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:?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: