一、题目:复杂链表的复制

题目:请实现函数ComplexListNode Clone(ComplexListNode head),复制一个复杂链表。在复杂链表中,每个结点除了有一个Next指针指向下一个结点外,还有一个Sibling指向链表中的任意结点或者NULL。

  结点的定义如下,采用C#语言描述:

    public class ComplexListNode
{
public int Data { get; set; }
public ComplexListNode Next { get; set; }
public ComplexListNode Sibling { get; set; } public ComplexListNode(int data)
{
this.Data = data;
} public ComplexListNode(int data, ComplexListNode next, ComplexListNode sibling = null)
{
this.Data = data;
this.Next = next;
this.Sibling = sibling;
}
}

  下图是一个含有5个结点的复杂链表。图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。为简单起见,指向NULL的指针没有画出。

二、解题思路

2.1 O(n2)的普通解法

  第一步是复制原始链表上的每一个结点,并用Next节点链接起来;

  第二步是设置每个结点的Sibling节点指针。

对于一个含有n个结点的链表,由于定位每个结点的Sibling都需要从链表头结点开始经过O(n)步才能找到,因此这种方法的总时间复杂度是O(n2)

2.2 借助辅助空间的O(n)解法

  第一步仍然是复制原始链表上的每个结点N创建N',然后把这些创建出来的结点用Next链接起来。同时我们把<N,N'>的配对信息放到一个哈希表中。

  第二步还是设置复制链表上每个结点的m_pSibling。由于有了哈希表,我们可以用O(1)的时间根据S找到S'

此方法使用空间换时间。对于有n个结点的链表我们需要一个大小为O(n)的哈希表,也就是说我们以O(n)的空间消耗把时间复杂度由O(n2)降低到O(n)

2.3 不借助辅助空间的O(n)解法

  第一步仍然是根据原始链表的每个结点N创建对应的N'。(把N'链接在N的后面)

    private static void CloneNodes(ComplexListNode head)
{
ComplexListNode node = head;
while (node != null)
{
ComplexListNode cloned = new ComplexListNode();
cloned.Data = node.Data;
cloned.Next = node.Next;
cloned.Sibling = null; node.Next = cloned;
node = cloned.Next;
}
}

  第二步设置复制出来的结点的Sibling。(把N'的Sibling指向N的Sibling)

    private static void ConnectSiblingNodes(ComplexListNode head)
{
ComplexListNode node = head;
while (node != null)
{
ComplexListNode cloned = node.Next;
if (node.Sibling != null)
{
cloned.Sibling = node.Sibling;
}
node = cloned.Next;
}
}

  第三步把这个长链表拆分成两个链表:把奇数位置的结点用Next链接起来就是原始链表,偶数数值的则是复制链表。

    private static ComplexListNode ReconnectNodes(ComplexListNode head)
{
ComplexListNode node = head;
ComplexListNode clonedHead = null;
ComplexListNode clonedNode = null; if (node != null)
{
clonedHead = clonedNode = node.Next;
node.Next = clonedNode.Next;
node = node.Next;
} while (node != null)
{
// 复制链表的连接
clonedNode.Next = node.Next;
clonedNode = clonedNode.Next;
// 原始链表的连接
node.Next = clonedNode.Next;
node = node.Next;
} return clonedHead;
}

  最后,将三个步骤衔接起来形成Clone方法:

    public static ComplexListNode Clone(ComplexListNode head)
{
CloneNodes(head);
ConnectSiblingNodes(head);
return ReconnectNodes(head);
}

三、单元测试

  辅助方法的封装

    public static void TestPortal(string testName, ComplexListNode head)
{
if (!string.IsNullOrEmpty(testName))
{
Console.WriteLine("{0} begins:", testName);
} Console.WriteLine("The original list is:");
PrintList(head); ComplexListNode clonedHead = Clone(head);
Console.WriteLine("The cloned list is:");
PrintList(clonedHead);
} private static void PrintList(ComplexListNode head)
{
ComplexListNode node = head;
while (node != null)
{
Console.WriteLine("The value of this node is: {0}.", node.Data);
if (node.Sibling != null)
{
Console.WriteLine("The value of its sibling is: {0}.", node.Sibling.Data);
}
else
{
Console.WriteLine("This node does not have a sibling.");
} Console.WriteLine(); node = node.Next;
}
} private static void BuildNodes(ComplexListNode node, ComplexListNode next, ComplexListNode sibling)
{
if (node != null)
{
node.Next = next;
node.Sibling = sibling;
}
}

  (1)Test1

    //          -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | | /|\ /|\
// --------+-------- |
// -------------------------
public static void Test1()
{
ComplexListNode node1 = new ComplexListNode();
ComplexListNode node2 = new ComplexListNode();
ComplexListNode node3 = new ComplexListNode();
ComplexListNode node4 = new ComplexListNode();
ComplexListNode node5 = new ComplexListNode(); BuildNodes(node1, node2, node3);
BuildNodes(node2, node3, node5);
BuildNodes(node3, node4, null);
BuildNodes(node4, node5, node2); TestPortal("Test1", node1);
}

  (2)Test2

    // Sibling指向结点自身
// -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | | /|\ /|\
// | | -- |
// |------------------------|
public static void Test2()
{
ComplexListNode node1 = new ComplexListNode();
ComplexListNode node2 = new ComplexListNode();
ComplexListNode node3 = new ComplexListNode();
ComplexListNode node4 = new ComplexListNode();
ComplexListNode node5 = new ComplexListNode(); BuildNodes(node1, node2, null);
BuildNodes(node2, node3, node5);
BuildNodes(node3, node4, node3);
BuildNodes(node4, node5, node2); TestPortal("Test2", node1);
}

  (3)Test3

    // Sibling形成环
// -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | /|\
// | |
// |---------------|
public static void Test3()
{
ComplexListNode node1 = new ComplexListNode();
ComplexListNode node2 = new ComplexListNode();
ComplexListNode node3 = new ComplexListNode();
ComplexListNode node4 = new ComplexListNode();
ComplexListNode node5 = new ComplexListNode(); BuildNodes(node1, node2, null);
BuildNodes(node2, node3, node4);
BuildNodes(node3, node4, null);
BuildNodes(node4, node5, node2); TestPortal("Test3", node1);
}

  (4)Test4

    // 只有一个结点
public static void Test4()
{
ComplexListNode node1 = new ComplexListNode();
node1.Sibling = node1; TestPortal("Test4", node1);
}

  (5)Test5

    // 鲁棒性测试
public static void Test5()
{
TestPortal("Test5", null);
}

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

剑指Offer面试题:24.复杂链表的复制的更多相关文章

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

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

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

    题目: 请实现函数ComplexListNode* Clone(ComplexListNode* pHead).复制一个复杂链表. 在复杂链表中.每个节点除了一个m_pNext指针指向下一个节点外,另 ...

  3. C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解

    剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...

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

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

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

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

  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= ...

  10. 剑指offer——面试题18:删除链表的节点

    #include"List.h" void DeleteNode(ListNode** pHead,ListNode* pToBeDeleted) { if(*pHead==nul ...

随机推荐

  1. 【Java EE 学习 67 上】【OA项目练习】【JBPM工作流的使用】

    OA项目中有极大可能性使用到JBPM框架解决流程控制问题,比如请假流程.报销流程等等. JBPM:JBoss Business Process Management,翻译过来就是业务流程管理.实际上就 ...

  2. DTMF三种模式(SIPINFO,RFC2833,INBAND)

    转自:http://www.tuicool.com/articles/n6Vb2iJ 1.DTMF(双音多频)定义:由高频音和低频音的两个正弦波合成表示数字按键(0~9 * # A B C D). 2 ...

  3. Redis集群搭建1

    wget .168.0.201:6379 192.168.0.201:6380 192.168.0.201:6381 192.168.0.202:16379 192.168.0.202:16380 1 ...

  4. JavaScript高级程序设计-(2)基础概念

    for-in 语句 for-in 语句是一种迭代语句,用来枚举对象属性,语法:for (property in expression) statement实例:for(var propName in ...

  5. DFS序+线段树 hihoCoder 1381 Little Y's Tree(树的连通块的直径和)

    题目链接 #1381 : Little Y's Tree 时间限制:24000ms 单点时限:4000ms 内存限制:512MB 描述 小Y有一棵n个节点的树,每条边都有正的边权. 小J有q个询问,每 ...

  6. 一个快速排序(分类)及使用类似思想实现选择问题[c++实现]

    一.快速排序(快速分类)算法: 问题描述:给定线性集中n个元素和一个整数k,1<=k<=n,要求找出这n个元素中第k小的元素. 思想:选取数组A中的某个元素 t=A[s],然后将其他元素重 ...

  7. webpack -p压缩打包react报语法错误处理

    前言:在用webpack打包react代码的时候发现一个问题,做一个处理总结. 我的webpack配置: var webpack = require('webpack'); //打包less插件 va ...

  8. XmlRpc.net 入参结构体嵌套的转义操作

    项目使用C#开发,需要使用XmlRpc和Linux服务器端交互,用的是XmlRpc.net. 普通的程序调用入参和出差都没有问题,今天遇到入参结构体嵌套,结果 args 入参在服务器端不能解析.抓包数 ...

  9. HDU 1394 Minimum Inversion Number ( 树状数组求逆序数 )

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 Minimum Inversion Number                         ...

  10. 用Node.js发送邮件

    本文讲的是用Node.js通过一个开启smtp的已有的邮箱账号发送邮件,而不是如何创建一个邮件服务器 开启smtp服务 首先要去要使用的邮箱中设置开启smtp,才能正常发送邮件 这边以163邮箱为例 ...