剑指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.将新复制的链表穿插到旧的链表中
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
2.这样子就行依据左右位置的旧的节点找到sibling位置了
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
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("\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:?
剑指offer面试题26-复杂链表的复制的更多相关文章
- 剑指offer 面试题35.复杂链表的复制
时间O(N),空间O(N) /* struct RandomListNode { int label; struct RandomListNode *next, *random; RandomList ...
- 剑指Offer - 九度1524 - 复杂链表的复制
剑指Offer - 九度1524 - 复杂链表的复制2014-02-07 01:30 题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点 ...
- 剑指Offer面试题:14.链表的倒数第k个节点
PS:这是一道出境率极高的题目,记得去年参加校园招聘时我看到了3次,但是每次写的都不完善. 一.题目:链表的倒数第k个节点 题目:输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题 ...
- 剑指offer面试题26:复杂链表的复制
题目:请实现一个函数,复制一个复杂链表. 在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个sibling指针指向链表中的任意结点或者nulL 直观解法: 1.遍历链表,复制链表节 ...
- 剑指Offer:面试题26——复制复杂的链表(java实现)
问题描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点). 思路1: 1.先复制链表节点,并用next链接起来. 2.然后对每一个结点去修改 ...
- 剑指Offer:面试题16——反转链表(java实现)
问题描述 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点.链表结点如下: public class ListNode { int val; ListNode next = n ...
- 【剑指offer 面试题15】链表中倒数第K个结点
思路: 定义两个指针同时指向head,第一个指针先走K-1步,随后二个指针同时移动,当第一个指针到末尾处时,第二个指针所指向的即为倒数第K个结点. #include <iostream> ...
- 剑指offer面试题15:链表中倒数第K个节点
题目:输入一个链表,输出该链表的倒数第K个节点.为了符合大多数人的习惯,本题从1开始计数,即链表尾节点是倒数第一个节点. 解题思路: 解法一:一般情况下,单向链表无法从后一个节点获取到它前面的节点,可 ...
- 剑指offer——面试题23:链表中环的入口节点
函数: ListNode* MeetingNode(ListNode* pHead) { if(pHead==nullptr) return nullptr; ListNode* quickNode= ...
随机推荐
- Android nomedia 避免图片等资源泄露在系统图库其中
总结 Android nomedia 避免文件泄露在系统图库和系统铃声中 在应用开发中 项目的图片总是被系统的图库收录了 避免图片被系统图库收录的发现有2个方法 第一种针对图片 将 .png为后缀的图 ...
- 初学ToggleButton 点击button,更换button背景图片;再次点击,恢复之前背景图
上方的图标,R.drawable.register_checked 是选中图片 下方的图标, R.drawable.register_unchecked 是未选中图片 默认是上方的选中效果.点击 ...
- webview同步cookies
目前很多android app都内置了可以显示web页面的界面,会发现这个界面一般都是由一个叫做WebView的组件渲染出来的,学习该组件可以为你的app开发提升扩展性. 先说下WebView的一些优 ...
- POJ 3252 组合数学?
大神们的题解我一个都没看懂........... 十分的尴尬 题意:算出闭区间内二进制中0的个数大于等于1的个数的数字有多少个 思路: 组合数学(n小于500的时候都可以出解,只不过高精比较麻烦). ...
- 视图 Model转集合
@{ Layout = null;}@using MvcApplication2.Models <!DOCTYPE html> <html><head> ...
- 构建工具系列二--Grunt
本文地址: http://www.cnblogs.com/blackmanba/p/frontend-scaffold-grunt.html或者http://forkme.info/frontend- ...
- .NET XML POST 请求
//请求体,XML参数 string xmlstring = @"<root></root>“; //请求URL string postUrl ="http ...
- pgpool中的配置参数的定义
/* * configuration parameters */typedef struct { char *listen_addresses; /* hostnames/ ...
- C++中static和const关键字的作用
static关键字至少有下列几个作用: 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值: 在模块内的static全局变量 ...
- Python多进程原理与实现
Date: 2019-06-04 Author: Sun 1 进程的基本概念 什么是进程? 进程就是一个程序在一个数据集上的一次动态执行过程.进程一般由程序.数据集.进程控制块三部分组成.我们编写 ...