题目:

请实现函数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-复杂链表的复制的更多相关文章

  1. 剑指offer 面试题35.复杂链表的复制

    时间O(N),空间O(N) /* struct RandomListNode { int label; struct RandomListNode *next, *random; RandomList ...

  2. 剑指Offer - 九度1524 - 复杂链表的复制

    剑指Offer - 九度1524 - 复杂链表的复制2014-02-07 01:30 题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点 ...

  3. 剑指Offer面试题:14.链表的倒数第k个节点

    PS:这是一道出境率极高的题目,记得去年参加校园招聘时我看到了3次,但是每次写的都不完善. 一.题目:链表的倒数第k个节点 题目:输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题 ...

  4. 剑指offer面试题26:复杂链表的复制

    题目:请实现一个函数,复制一个复杂链表. 在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个sibling指针指向链表中的任意结点或者nulL 直观解法: 1.遍历链表,复制链表节 ...

  5. 剑指Offer:面试题26——复制复杂的链表(java实现)

    问题描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点). 思路1: 1.先复制链表节点,并用next链接起来. 2.然后对每一个结点去修改 ...

  6. 剑指Offer:面试题16——反转链表(java实现)

    问题描述 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点.链表结点如下: public class ListNode { int val; ListNode next = n ...

  7. 【剑指offer 面试题15】链表中倒数第K个结点

    思路: 定义两个指针同时指向head,第一个指针先走K-1步,随后二个指针同时移动,当第一个指针到末尾处时,第二个指针所指向的即为倒数第K个结点. #include <iostream> ...

  8. 剑指offer面试题15:链表中倒数第K个节点

    题目:输入一个链表,输出该链表的倒数第K个节点.为了符合大多数人的习惯,本题从1开始计数,即链表尾节点是倒数第一个节点. 解题思路: 解法一:一般情况下,单向链表无法从后一个节点获取到它前面的节点,可 ...

  9. 剑指offer——面试题23:链表中环的入口节点

    函数: ListNode* MeetingNode(ListNode* pHead) { if(pHead==nullptr) return nullptr; ListNode* quickNode= ...

随机推荐

  1. Android 推断程序在手机中是否是活动状态或者正在执行状态

    沈阳斌子在今天项目需求上碰到个这种问题,在Service中须要推断当前的程序是否是活动状态,换句话说也就是说后台跑的服务中有业务需求检測当前程序是否是该服务的程序 这样好让点击推送通知时跳转到不同的页 ...

  2. mysql安装出错cannot create windows service for mysql.error:0

    配置时最后一步出现不能启动mysql 解决成功的办法:[MySQL] Could not start the service MySQL 解决方法 安装mysql 5.1.33,在运行Server I ...

  3. 32.QT绘图

    widget.h #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QPainter> #inclu ...

  4. 将查询到的数据导出到Excel终结版

    吐槽 最近新项目需要用到导出数据到Excel,试了试之前写的一篇博文,但是感觉那个不太好,主要原因是没能实现样式控制,今天我们就来介绍一种新的导出Excel方法,而且这种方法很轻量级,它利用xml生成 ...

  5. User_Login_Register_Shopping 1.0

    #!/usr/bin/env python# -*- coding: utf-8 -*-# @File : user_login_register_shopping.py# @Author: Anth ...

  6. iOS11中navigationBar上 按钮图片设置frame无效 不受约束 产生错位问题 解决

    问题描述: 正常样式: 在iOS 11 iPhone X上显示效果: 观察顶部navBar上的左侧按钮  在ios 11 上  这个按钮的图片不受设置的尺寸约束,按其真实大小展示,造成图片错位,影响界 ...

  7. MySQL查询结果保存到本地

    #!/bin/bash mysql -h<公网IP> -P<端口号> -u<用户名> -p<密码> -D<指定数据库> >/Users ...

  8. 路飞学城Python-Day50

    05-运算符 常用运算符 算数运算符 赋值运算符 比较运算符 逻辑运算符         // 赋值运算符          var money = prompt('请输入金额');          ...

  9. BZOJ 1016 最小生成树计数(矩阵树定理)

    我们把边从小到大排序,然后依次插入一种权值的边,然后把每一个联通块合并. 然后当一次插入的边不止一条时做矩阵树定理就行了.算出有多少种生成树就行了. 剩下的交给乘法原理. 实现一不小心就会让程序变得很 ...

  10. MHA 主从切换过程及日志分析

    本文主要在MHA 切换日志的角度分析MHA切换的过.MHA故障切换过程如下图所示 第一部分:开启MHA 监控 通过分析日志,得到以下步骤: 1.读取MHA manager 节点的配置文件,并检查配置文 ...