《剑指offer》面试题26 复杂链表的复制 Java版

(定义一个新的数据结构,每个节点除了具有普通链表的next域外,还有一个额外的引用指向任意节点。我们要对由该特殊数据结构形成的链表进行复制。)

我的方法:也就是克隆一个这种特殊链表,很快想到先不考虑原链表sibling域,复制出一个新的链表,然后再去给sibling域赋值。由于sibling可以指向任何节点,而且我们是根据原链表的sibling来确定新链表中的sibling,所以每次我们寻找新链表中某个节点的sibling,都要两个指针重新从头开始扫描以确定新链表中的sibling。所以时间复杂度是O(n)+O(n^2)。

        public ComplexListNode complexClone(ComplexListNode head){
            if(head == null)return null;
            ComplexListNode headMark = head;
            ComplexListNode newHeadMark = new ComplexListNode(head.val);
            ComplexListNode newHead = newHeadMark;
            head = head.next;

            //仅形成next域
            for(; head != null; head = head.next){
                newHead.next = new ComplexListNode(head.val);
                newHead = newHead.next;
            }
            //形成sibling域
            head = headMark;
            newHead = newHeadMark;
            for(; head!=null; head=head.next, newHead=newHead.next){
                ComplexListNode index = headMark;
                ComplexListNode newIndex = newHeadMark;
                for(; index!=null; index=index.next,newIndex=newIndex.next){
                    if(head.sibling == index){
                        newHead.sibling = newIndex;
                    }
                }
            }
            return newHeadMark;
        }

书中方法一:一看书,果然上面的方法是最烂的,主要时间复杂度集中在第二步确定sibling上面,我们是否能够让每次寻找的时间复杂度减小到O(1)?一般来说牺牲空间复杂度可以降低时间复杂度+一次寻找的时间复杂度是O(1),我们就想到了HashMap。如何使用呢?我们在创建新链表的时候存储原链表每个节点对应的新链表节点,如(old,new),这样在第二步连接sibling的时候就可以根据原链表节点一步找到新链表节点。

        public ComplexListNode complexClone2(ComplexListNode head){
            if(head == null)return null;
            Map<ComplexListNode, ComplexListNode> map = new HashMap<>();
            ComplexListNode headMark = head;
            ComplexListNode newHeadMark = new ComplexListNode(head.val);
            ComplexListNode newHead = newHeadMark;
            map.put(headMark, newHead);

            head = head.next;
            for(; head!=null; head = head.next){
                newHead.next = new ComplexListNode(head.val);
                map.put(head, newHead.next);
                newHead = newHead.next;
            }
            for(ComplexListNode index = headMark,newIndex = newHeadMark;
                    index!=null; index=index.next, newIndex=newIndex.next){
                newIndex.sibling = map.get(index.sibling);
            }
            return newHeadMark;
        }

书中方法二:书上还讲了一种时间复杂度O(n)空间复杂度O(1)的方法。我们既可以根据原链表的节点迅速确定新链表的节点(即和HashMap一样也存在一种一一对应的关系),又不用额外创建空间。创建新链表时,把新链表的对应节点放在原链表节点的后面可以达到一一对应的效果,最后我们把一整条链表拆开,这样也不会破坏源链表结构,也得到了新链表。从本质上来讲,如果我们完全抛弃原链表的结构去寻找sibling,相当于丢弃了一部分信息,也就是把所有节点当成一个set去遍历寻找。如果考虑了sibling的结构(即把新的节点创建在原节点之后),相当于走了捷径。

        public ComplexListNode complexClone3(ComplexListNode head){
            copyAndConstruct(head);
            linkSibling(head);
            return unpackage(head);
        }
        private void copyAndConstruct(ComplexListNode head){
            ComplexListNode index = head;
            while(index != null){
                ComplexListNode temp = new ComplexListNode(index.val);
                temp.next = index.next;
                index.next = temp;
                index = index.next.next;
            }
        }
        private void linkSibling(ComplexListNode head){
            ComplexListNode index = head;
            while(index != null){
                if(index.sibling == null){
                    index.next.sibling = null;
                }else{
                    index.next.sibling = index.sibling.next;
                }
                index = index.next.next;
            }
        }

        private ComplexListNode unpackage(ComplexListNode head){
            if(head == null)return null;
            ComplexListNode newIndex = head.next;
            ComplexListNode newHead = newIndex;
            ComplexListNode index = head;
            while(index != null){
                index.next = newIndex.next;
                if(newIndex.next != null){
                    newIndex.next = newIndex.next.next;
                }
                index = index.next;
                newIndex = newIndex.next;
            }
            return newHead;
        }

原文地址:https://www.cnblogs.com/czjk/p/11686695.html

时间: 2024-11-11 21:48:42

《剑指offer》面试题26 复杂链表的复制 Java版的相关文章

剑指offer面试题笔记11~20题(Java实现)

一.面试题1:复制运算符函数(P24) 题目:如下为类型CMString的声明,请为该类型添加赋值运算符函数. class CMyString { public: CMyString(Char* pData = NULL); CMyString(const CMyString& str); ~CMyString(void); private: char* m_pData; } 解题思路: 二.面试题2:实现Singleton模式(P31) 题目:设计一个类,我们只能生成该类的一个实例. 解题思路

【剑指Offer】25、复杂链表的复制

题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) 题解:Hashmap 1 public static RandomListNode Clone(RandomListNode pHead){ 2 if(pHead==null){ 3 return pHead; 4 } 5 RandomListNode oldP= pHea

【剑指Offer面试题】 九度OJ1516:调整数组顺序使奇数位于偶数前面

题目链接地址: http://ac.jobdu.com/problem.php?pid=1516 题目1516:调整数组顺序使奇数位于偶数前面 时间限制:1 秒内存限制:128 兆特殊判题:否提交:2858解决:924 题目描写叙述: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得全部的奇数位于数组的前半部分,全部的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 输入: 每一个输入文件包括一组測试案例. 对于每一个測试案例.第一行输入一个n,代表该数组

二叉树层次遍历(剑指Offer面试题32:从上到下打印二叉树)

图1所示为二叉树的层次遍历,即按照箭头所指方向,按照1.2.3的层次顺序,对二叉树每个节点进行访问 (此图反映的是自左至右的层次遍历,自右至左的方式类似). 要进行层次遍历,需要建立一个队列.先将二叉树头节点入队列,然后出队列,访问该节点, 如果它有左子树,则将左子树的根结点入队:如果它有右子树,则将右子树的根结点入队.然后出队列,对出队节点访问, 如此反复直到队列为空为止. 1 import java.util.*; 2 class TreeNode 3 { 4 int val; 5 Tree

剑指offer面试题29:数组中出现次数超过一半的数字

题目:数组中有一个数字出现的次数超过数组长度的一般,请找出这个数字,例如输入一个长度为9的数组(1,2,3,2,2,2,5,4,2,).由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 个人第一眼想法是通过一个sort函数,再判断中间那数出现次数,只要出现多于n/2,就直接输出. 一般来说,最为直观的算法面试官都不会满意,那么有没有更优的算法呢? 这种算法是受快速排序算法的启发.在随机快速排序算法中,我们现在数组中随机选择一个数字,然后调整数组中数字的顺序,使得比选中的数字小的数字

【剑指Offer面试题】二维数组中的查找

下决心AC所有剑指offer面试题. 九度OJ面试题地址:http://ac.jobdu.com/hhtproblems.php 书籍:何海涛--<剑指Offer:名企面试官精讲典型编程题> 对于面试题,面试官往往更希望我们能提出优化方法,这样更能体现我们的思维能力以及传说中的"内功".所以做剑指offer要着重训练这方面,多总结多细究,总是有好处的.加油~ 二维数组中的查找 时间限制:1 秒内存限制:32 兆 特殊判题:否提交:19005解决:3642 题目描述: 在一个

【剑指Offer面试题】九度OJ1384:二维数组中的查找

下决心AC全部剑指offer面试题. 九度OJ面试题地址:http://ac.jobdu.com/hhtproblems.php 书籍:何海涛--<剑指Offer:名企面试官精讲典型编程题> 对于面试题,面试官往往更希望我们能提出优化方法,这样更能体现我们的思维能力以及传说中的"内功".所以做剑指offer要着重训练这方面,多总结多细究,总是有优点的.加油~ 题目链接地址: http://ac.jobdu.com/problem.php?pid=1384 二维数组中的查找

【剑指Offer面试题】 九度OJ1386:旋转数组的最小数字

题目链接地址: http://ac.jobdu.com/problem.php?pid=1386 题目1386:旋转数组的最小数字 时间限制:1 秒内存限制:32 兆特殊判题:否提交:6914解决:1534 题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. 输入: 输入可能包含多个测试样例,对于每个测试案例, 输入的第一行为

【剑指offer】Q16:翻转链表

def reverse(head): if head == None or head.next == None: return head psuhead = ListNode(-1) while head: nexthead = head.next head.next = psuhead.next psuhead.next = head head = nexthead head = psuhead.next del psuhead return head [剑指offer]Q16:翻转链表