链表:常见面试题-拷贝特殊链表

该文章描述了一种特殊的单链表节点类,包含value、next和random三个指针。random指针可以指向链表中的任意节点或null。文章提出了两种复制这种链表的方法,一种使用HashMap,另一种仅使用有限变量。文章提供了使用有限变量的详细实现步骤,包括复制节点、设置random指针和拆分链表的过程,并附有代码示例。
摘要由CSDN通过智能技术生成

题目:

一种特殊的单链表节点类描述如下:

class Node {

        int value;

        Node next;

        Node rand;

        Node(int val) {value = val}

}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点(包括自己),也可能指向null.

给定一个由Node节点类型组成的无环单链表的头结点head,请实现一个函数完成复制这个链表,并返回新链表的头结点。

要求:时间复杂度O(N),额外空间复杂度O(1)

思路:这个题仍然有两种解法:利用额外容器或者使用有限几个变量,具体思路:

(1)使用额外的容器,使用一个HashMap,把当前链表的每一个Node作为key装入HashMap,value是它的拷贝节点,都装完之后依次取出每一个Node,把value(拷贝节点)的next和random分别指向HashMap中取出的以当前节点的next和random为key的拷贝节点,没有

(2)不使用额外的容器,只使用有限几个变量,先遍历链表,依次拷贝Node然后把拷贝节点放在当前当前节点和原来的next中间。 然后依次遍历链表,设置各个拷贝节点的random指针指向。最后把两个链表进行拆分.

第一种方法面试没有分且笔试的空间复杂度为O(N),笔试也过不了,这里我们不做介绍,有兴趣的可以自己写写玩。

第二种方法的拆分示意图:

对应的代码如下:

package dataStructure.linkedList.practice;

import dataStructure.linkedList.ListNodeWithRandom;

public class CopyLinkedListWithRandom {
    public static ListNodeWithRandom copyLinkedList(ListNodeWithRandom head) {
        if(head == null) return head;
        ListNodeWithRandom cur = head;
        ListNodeWithRandom next = null;
        //把每个节点复制一个放在当前节点和当前节点的next之间,最后一个copy放在当前节点和null之间
        //例如1->2->3变成1->11->2->22->3->33
        while(cur != null) {
            next = cur.next;
            cur.next = new ListNodeWithRandom(cur.val, cur.name+"'");
            cur.next.next = next;
            cur = next;
        }

        cur = head;
        //设置random指针
        while(cur != null) {
            //取到当前Node的random指针
            ListNodeWithRandom random = cur.random;
            //当前random如果为空,那拷贝节点的random也应该为空
            //如果当前random不为空,则random的下一个(原链表中random的拷贝)就是拷贝节点的random
            cur.next.random = random == null? null : random.next;
            //cur.next.next是原链表中cur的下一个节点
            cur = cur.next.next;
        }
        //分离原链表和拷贝链表
        //1->11->2->22->3->33 分离为1->2->3和11->22->33
        cur = head;
        ListNodeWithRandom newHead = null;
        while(cur != null) {
            //取到cur的拷贝节点
            ListNodeWithRandom curCopy = cur.next;
            //如果当前节点是头节点,则它的拷贝就是新链表的头
            if(newHead == null) {
                newHead = curCopy;
            }
            //cur的下一个节点就是curCopy的下一个节点
            cur.next = curCopy.next;
            //curCopy的next取决于cur.next是否为空,如果为空,则它的next也应该为空
            //如果不为空,根据规律我们应该取cur.next的下一个节点(cur.next的拷贝)
            curCopy.next = cur.next == null? null : cur.next.next;
            //这个时候cur.next已经是cur的原链表的下一个节点了
            cur = cur.next;
        }
        return newHead;
    }

    public static void main(String[] args) {
        ListNodeWithRandom n1 = new ListNodeWithRandom(1, "1");
        ListNodeWithRandom n2 = new ListNodeWithRandom(2,"2");
        ListNodeWithRandom n3 = new ListNodeWithRandom(3, "3");
        n1.next = n2;
        n2.next = n3;
        n1.random = n3;
        n2.random = n1;
        n3.random = n2;
        ListNodeWithRandom newHead = copyLinkedList(n1);
        while(newHead != null) {
            if(newHead.next != null) {
                System.out.print(newHead + " ->");
            } else {
                System.out.print(newHead);
            }
            newHead = newHead.next;

        }
    }
}

运行结果:

{val=1, random=3', name='1''} ->{val=2, random=1', name='2''} ->{val=3, random=2', name='3''}

粗略代码,难免出错,欢迎批评指正

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值