链表05--复杂链表的复制

链表05--复杂链表的复制-jz25

题目概述

  • 算法说明
    输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
  • 测试用例
    输入:
    {1,2,3,4,5,3,5,#,2,#}
    {1,2,3,4,5,#,5,3,2,#}
    {1,2,3,4,5,#,4,#,2,#}
    {1,#}
    {}
    输出:
    {1,2,3,4,5,3,5,#,2,#}
    {1,2,3,4,5,#,5,3,2,#}
    {1,2,3,4,5,#,4,#,2,#}
    {1,#}
    {}

解析&参考答案

  • 解析
    • 方法1:原始节点后追加法
      1. 复制链表节点,使原链表1-2-3变为1-1’-2-2’-3-3’
      2. 复制random 指针,需要考虑random 为nil的情况
      3. 分离出拷贝的节点,提取出1’-2’-3’
    • 方法2:哈希表
      1. 在map中存储m[原始节点]=拷贝节点
      2. 将拷贝节点构成一个链表
  • 参考答案
vim jz25.go
package main

import "fmt"

type RandomListNode struct {
	Label  int
	Next   *RandomListNode
	Random *RandomListNode
}

func PrintList(head *RandomListNode) {
	if head == nil {
		fmt.Print("nil")
	} else {
		p := head
		for p != nil {
			if p.Next != nil {
				fmt.Printf("%d->", p.Label)
			} else {
				fmt.Printf("%d\n", p.Label)
			}
			p = p.Next
		}
	}
}

func Clone(head *RandomListNode) *RandomListNode {
	if head == nil {
		return head
	}
	p := head
	// step1: 复制每个节点,并将其插入到该节点之后
	for p != nil {
		tmp := &RandomListNode{p.Label, p.Next, nil}
		p.Next = tmp
		p = tmp.Next
	}
	//PrintList(head)
	// step2: 为step1 中每个节点设置 Random指向(其上 一个节点的 Random 的Next节点),此处需要判断p和p.random是否为nil
	p = head
	for p != nil {
		if p.Random != nil {
			p.Next.Random = p.Random.Next
		}
		p = p.Next.Next
	}
	// step3: 取出偶数节点,该节点就是目标节点
	p = head
	headNew := head.Next
	for p != nil {
		cloneNode := p.Next
		p.Next = cloneNode.Next
		if cloneNode.Next != nil {
			cloneNode.Next = cloneNode.Next.Next
		}
		p = p.Next
	}
	return headNew
}

func CloneV2(head *RandomListNode) *RandomListNode {
	// 使用map 在牛客的oj 上提示超时,有看出错误原因的读者可以帮忙优化下
	if head == nil {
		return head
	}
	m := make(map[*RandomListNode]*RandomListNode)
	p := head
	for p != nil {
		m[p] = &RandomListNode{p.Label, nil, nil}
		p = p.Next
	}
	p = head
	for p != nil {
		if p.Next == nil {
			m[p].Next = nil
		} else {
			m[p].Next = m[p.Next]
		}
		if p.Random == nil {
			m[p].Random = nil
		} else {
			m[p].Random = m[p.Random]
		}
	}
	return m[head]
}

func main() {
	p0 := &RandomListNode{0, nil, nil}
	p1 := &RandomListNode{1, nil, nil}
	p2 := &RandomListNode{2, nil, nil}
	p3 := &RandomListNode{3, nil, nil}
	p4 := &RandomListNode{4, nil, nil}
	p0.Next, p0.Random = p1, p2
	p1.Next, p1.Random = p2, p4
	p2.Next, p2.Random = p3, p3
	p3.Next, p3.Random = p4, p1
	p4.Next, p4.Random = nil, nil
	PrintList(p0)
	head := Clone(p0)
	PrintList(head)
}

注意事项

  1. to add

说明

  1. 当前使用 go1.15.8
  2. 参考 牛客网--剑指offer
    标题中jzn(n为具体数字)代表牛客网剑指offer系列第n号题目,例如 jz01 代表牛客网剑指offer中01号题目。

注意!!!

  • 笔者最近在学习 golang,因此趁机通过数据结构和算法来进一步熟悉下go语言
  • 当前算法主要来源于剑指 offer,后续会进一步补充 LeetCode 上重要算法,以及一些经典算法
  • 此处答案仅为参考,不一定是最优解,欢迎感兴趣的读者在评论区提供更优解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昕光xg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值