[算法学习]复杂链表的复制
2016-02-18 13:56
387 查看
问题描述: 复制一个复杂链表。 在复杂链表中,每个结点除了有一个指向下一个结点的next指针外,还有一个 sibing指针 指向链表中任意结点或者NULL。
1. 遍历第一遍数组,把复制链的next和各结点值复制好。需要时间O(n)
2. 第二遍遍历,二重for循环根据原链表,将复制链的slibing指针连接好。需要时间O(n^2)
3. 需要时间复杂度是O(n^2),空间复杂度是O(1)
1. 使用一个HashMap(Key:原链表上的结点,Value:复制链表上的结点),空间复杂度是O(n)
2. 遍历第一遍数组:每次创建复制链的结点时,需要先查找HashMap中结点是否存在,不存在才创建,然后保存结点到HashMap中。需要时间O(n)
4. 需要时间复杂度是O(n),空间复杂度是O(n)
1. 遍历原链表。在原链表的每个结点的后面插入对应复制结点,将原链表“伸长”1倍;
2. 遍历原链表。将复制结点的slibing连接好;
3. 遍历原链表。将原结点和复制结点“分裂”开,将next连接好;
4. 需要时间复杂度是O(n),空间复杂度是O(1)
附:源码地址
解法一:“爆破”
解法分析:1. 遍历第一遍数组,把复制链的next和各结点值复制好。需要时间O(n)
2. 第二遍遍历,二重for循环根据原链表,将复制链的slibing指针连接好。需要时间O(n^2)
3. 需要时间复杂度是O(n^2),空间复杂度是O(1)
参考代码如下
[code]/** * 复杂链表的结点 * * @author kesar * */ static class Node { int val; Node next; Node slibing; public Node(){} public Node(int val) { this.val = val; } @Override public String toString() { return "Node [val=" + val + "]"; } } /** * 第一种解法:暴力复制。复制一遍next的,再每个结点遍历一遍进行slibing的比较。 时间复杂度是:O(n^2),空间复杂度是:O(1) * * @param oHead * @return */ public static Node copy1(Node oHead) { if (oHead == null) { return null; } // 复制的链表的头结点 Node cHead = new Node(oHead.val); // oMove是原链表上遍历用的移动指针,cMove是复制链表上遍历用的移动指针 // 复制Node结点的值以及next链关系 for (Node oMove = oHead.next, cMove = cHead; oMove != null; cMove = cMove.next, oMove = oMove.next) { cMove.next = new Node(oMove.val); } // 复制slibing的链关系 for (Node oMove = oHead, cMove = cHead; oMove != null; cMove = cMove.next, oMove = oMove.next) { if (oMove.slibing == null) continue; // 查找开始: 遍历查找slibing所指原链表结点对应在copy链表中的结点 Node findNode = cHead; for (; findNode != null && findNode.val != oMove.slibing.val; findNode = findNode.next) ; // 查找结束:findNode就是结果 cMove.slibing = findNode; } return cHead; }
解法二:使用辅助空间
解法分析:1. 使用一个HashMap(Key:原链表上的结点,Value:复制链表上的结点),空间复杂度是O(n)
2. 遍历第一遍数组:每次创建复制链的结点时,需要先查找HashMap中结点是否存在,不存在才创建,然后保存结点到HashMap中。需要时间O(n)
4. 需要时间复杂度是O(n),空间复杂度是O(n)
参考代码如下
[code]/** * 第二种解法:利用HashMap的O(1)的高效率查找来取代第一中解法中的一个for循环。用O(n)的空间换来O(n)的时间。时间复杂度O(n),空间复杂度:O(n) * * @param oHead * @return */ public static Node copy2(Node oHead) { if (oHead == null) { return null; } // 辅助空间:Key:原链表上的结点,Value:复制链表上的结点 HashMap<Node, Node> map = new HashMap<Node, Node>(); // 初始化复制链表的头结点 Node cHead = new Node(oHead.val); map.put(oHead, cHead); if (oHead.slibing != null) { cHead.slibing = new Node(oHead.slibing.val); map.put(oHead.slibing, cHead.slibing); } // 作用:遍历原链表,复制结点到复制链表中。 // 思路是这样:先从map找原结点对应的复制结点,如果没找到,就创建一个,然后连接到复制链表中,然后put入map中,便于其他时候使用 for (Node oMove = oHead.next, cMove = cHead; oMove != null; oMove = oMove.next, cMove = cMove.next) { if (map.containsKey(oMove)) { cMove.next = map.get(oMove); } else { cMove.next = new Node(oMove.val); map.put(oMove, cMove.next); } Node slibing = oMove.slibing; if (slibing == null) { continue; } if (map.containsKey(slibing)) { cMove.next.slibing = map.get(slibing); } else { cMove.next.slibing = new Node(slibing.val); map.put(slibing, cMove.next.slibing); } } return cHead; }
解法三:“伸长分裂法”
解法分析:1. 遍历原链表。在原链表的每个结点的后面插入对应复制结点,将原链表“伸长”1倍;
2. 遍历原链表。将复制结点的slibing连接好;
3. 遍历原链表。将原结点和复制结点“分裂”开,将next连接好;
4. 需要时间复杂度是O(n),空间复杂度是O(1)
参考代码如下
[code]/** * @param oHead * @return */ public static Node copy3(Node oHead) { if (oHead == null) { return null; } // 1."伸长" for (Node oMove = oHead; oMove != null; oMove = oMove.next) { // 复制结点 Node copy = new Node(oMove.val); // 插入复制结点到原结点的后面 copy.next = oMove.next; oMove.next = copy; // 移动到复制结点处 oMove = copy; } // 2.连接好slibing for (Node oMove = oHead; oMove != null; oMove = oMove.next) { if (oMove.slibing == null) { continue; } Node copy = oMove.next; copy.slibing = oMove.slibing.next; oMove = copy; } // 3."分裂",连接好原链表的next和复制链表中的next Node cHead = oHead.next; oHead.next = cHead.next; cHead.next = null; for (Node oMove = oHead.next, cMove = cHead; oMove != null; oMove = oMove.next, cMove = cMove.next) { Node copy = oMove.next; oMove.next = copy.next; copy.next = null; cMove.next = copy; } return cHead; }
附:源码地址
相关文章推荐
- [算法学习]求链表的中间结点
- jQuery自学教程(一)
- 获取字符串中指定位置开始的指定长度的字符串,支持汉字英文混合 汉字为2字节计数
- Dynamics AX Hostory
- Java线程(十):CAS
- 【python】编程语言入门经典100例--3
- Ubuntu安装gitlab
- [算法学习]求出链表中倒数第n个节点
- 加密算法介绍
- Centos YUM 升级PHP至5.3.3
- [Unity]Android(安卓)代码安卓APK时,使用系统安装器安装的[最正确]办法(不弹出选择打开的程序)
- 黑客魔术!如何黑掉一台根本不联网的电脑
- android进程间通信之Broadcast
- 为什么 volatile 关键字不能在 kernel 中使用(linux kernel 文档翻译)
- [算法学习]判断链表是否是环结构
- Java中通过UDP协议发送和接受数据
- linux 笔记
- Java中通过UDP协议发送和接受数据
- 1.基础部分
- 常用的抓包软件