【LeetCodehot100】 T138 随机链表的复制 T148: 排序链表

T138 随机链表的复制

这道题刚开始看很难理解,我们可以先看答案,跟着实例理解题目在干什么然后来分析这道题的小巧思
题目理解:

这道题和常见的链表不同

常见链表:

1 → 2 → 3 → 4 → 5

每个节点只有一个指针:next

这道题多了一个指针

java 复制代码
class Node {
    int val;
    Node next;
    Node random;
}

也就是说 每个节点有两个指针

指针 作用
next 指向下一个节点
random 可以指向链表中任意节点

random 可以随便指

举个例子:

假设链表是:

1 → 2 → 3

next关系:

1.next = 2

2.next = 3

3.next = null

random关系可能是:

1.random → 3

2.random → 1

3.random → 2

题目要干什么

复制一份一模一样的链表

这包含了两个重要信息

  1. 一模一样
    复制后的链表结构要一样:

next关系一样

random关系一样

  1. 深拷贝

复制出来的节点必须是:

新的节点

而不是原来的节点。

例如:

原链表:

A → B → C

复制后:

A' → B' → C'

并且:

A'.random 指向 C'

B'.random 指向 A'

而不是:

A'.random 指向 C ❌

题目的本质

想成为本质高手,就理解一句话:

复制一个带 random 指针的链表

核心思想(HashMap映射)

建立映射关系:

旧节点 → 新节点

例如:

1 → 1'

2 → 2'

3 → 3'

存储在:

HashMap<Node, Node>

整体流程

算法分 两次遍历

第一遍:创建所有新节点
第二遍:连接 next 和 random

流程图:

代码实现

java 复制代码
if(head==null)return null;
Map<Node,Node> map=new HashMap<>;
Node cur=head;

//第一步:复制所有节点
while(cur!=null){
map.put(cur,new Node(cur.val))
cur=cur.next;
}

//第二步:连接next和random
while(cur!=null){
Node newNode=map.get(cur);
newNode.next=map.get(cur.next);
newNode.random=map.get(cur.random);

cur=cur.next;
}
return map.get(head);

题目小坑

在最后return阶段

return map.get(head);;

而不是:

return head;

题目要求返回复制后的链表,即为:return map.get(head);

本题感悟

理解了Hash Map的映射关系

map.put(cur,new Node(cur.val)) // 创建了(1,1')

实现两个节点连接的方式

newNode.next=map.get(cur.next); // next和next的连接

newNode.random=map.get(cur.random); // random和random的连接

T148: 排序链表

题目要求:

给你一个链表,把链表 排序

题目要求很简单,但是这题难在找到正确的算法结构,否则很容易超时

题目真正的限制

时间复杂度:O(n log n)

空间复杂度:O(1)(尽量)

所以不能用:

数组排序

Collections.sort

因为:链表随机访问慢

寻找算法

排序 适不适合链表
快速排序 不太适合
归并排序 非常适合

归并才是最终选择

链表不支持随机访问
但非常适合拆分和合并

归并排序的理解

归并有三个步骤:

1 拆分链表

2 递归排序

3 合并两个有序链表

例如:

4 → 2 → 1 → 3

拆成:

4 → 2

1 → 3

再拆:

4 2 1 3

然后开始合并:

4 和 2 → 2 → 4

1 和 3 → 1 → 3

最后:

2 → 4

1 → 3

合并:

1 → 2 → 3 → 4

如何拆成两半?

核心技巧:

快慢指针

java 复制代码
while(fast != null && fast.next != null){
    slow = slow.next;
    fast = fast.next.next;
}

最终:

slow 在中点

然后断开链表:

ListNode mid = slow.next;

slow.next = null;

代码实现

java 复制代码
public ListNode sortList(ListNode head) {
    if(head == null || head.next == null){
        return head;
    }

    // 找中点
    ListNode slow = head;
    ListNode fast = head.next;

    while(fast != null && fast.next != null){
        slow = slow.next;
        fast = fast.next.next;
    }

    ListNode mid = slow.next;
    slow.next = null;

    // 递归排序
    ListNode left = sortList(head);
    ListNode right = sortList(mid);

    // 合并
    return merge(left, right);
}
//把两个有序链表合并成一个有序链表
private ListNode merge(ListNode l1, ListNode l2){
    ListNode dummy = new ListNode(0);
    ListNode cur = dummy;

    while(l1 != null && l2 != null){
        if(l1.val < l2.val){
            cur.next = l1;
            l1 = l1.next;
        }else{
            cur.next = l2;
            l2 = l2.next;
        }
        cur = cur.next;
    }

    if(l1 != null) cur.next = l1;
    if(l2 != null) cur.next = l2;

    return dummy.next;
}

本题感悟

理解的归并排序的使用场景:

因为链表不支持随机访问,但非常适合拆分和合并。而归并有着拆分和合并的特点,使用选择他

理解归并的流程:

1 快慢指针找中点

java 复制代码
slow = slow.next
fast = fast.next.next

2 链表拆分

java 复制代码
slow.next = null

断开链表。

3 合并有序链表

经典技巧:

dummy + 双指针

相关推荐
LuminousCPP3 分钟前
数据结构 - 线性表第三篇:基于顺序表实现 C 语言通讯录(基础功能篇)
c语言·数据结构·经验分享·笔记·算法
开压路机1 小时前
数据结构:图
数据结构·算法
小挪号底迪滴2 小时前
研发出海实战:多语言字符渲染陷阱、异构文件解析与跨国协作指南
css·数据结构·ai
zero.cyx2 小时前
软件设计师(3)数据结构
数据结构
夏日听雨眠3 小时前
数据结构(堆排序,基数排序)
数据结构·算法
akarinnnn3 小时前
深入理解内存函数:原理、应用与优化
c语言·网络·数据结构·算法
一行代码一行诗++3 小时前
for循环中的break和continue
数据结构·算法
故事和你913 小时前
洛谷-【图论2-4】连通性问题1
开发语言·数据结构·c++·算法·动态规划·图论
爱炼丹的James3 小时前
第三章 搜索和图论
数据结构·算法·图论
努力努力再努力wz4 小时前
【QT入门系列】QWidget 六大常用属性详解:windowOpacity、cursor、font、focus、toolTip 与 styleSheet
android·开发语言·数据结构·c++·qt·mysql·算法