链表专题-03

链表专题(三)

两数相加

问题

力扣2\] [2. 两数相加 - 力扣(LeetCode)](https://leetcode.cn/problems/add-two-numbers/description/)

问题描述

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

java 复制代码
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

java 复制代码
输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

java 复制代码
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

解决方案

定义两个链表的临时索引: curr1,curr2。

定义第三个链表的虚拟头结点: dummy,及其临时索引curr3。

从第一个、二个节点中拆一个节点计算值并存入第三个链表

tips: 需要记录两数相加的进位up(0/1)

java 复制代码
int t = 0;
if (curr1 == null) { //第一个链表运算结束
    t = curr2.val + up;
    curr2 = curr2.next;
} else if (curr2 == null) { //第二个链表运算结束
    t = curr1.val + up;
    curr1 = curr1.next;
} else { //两个链表运算都没有结束
    t = curr1.val + curr2.val + up;
    curr1 = curr1.next;
    curr2 = curr2.next;
}
up = t / 10;   //计算进位值(0,1)
curr3.next = new ListNode(t % 10); //计算新节点的值大于10则取余
curr3 = curr3.next;

如果计算完成后超出链表的长度(即计算的结果超出了链表的长度),需要额外的添加节点存储进位up(up=1)

if(up ==1){

curr3.next = new ListNode(up);

}

参考实现

java 复制代码
/**
 * 遍历两个链表将两个链表相加添加到第三个链表节点上
 */
public ListNode addTwoNumbers(ListNode head1, ListNode head2) {
    int up = 0;
    ListNode dummy = new ListNode(-1); //虚拟头结点记录第三个链表(head未知)
    ListNode curr1 = head1;
    ListNode curr2 = head2;
    ListNode curr3 = dummy;

    /**
     *  两个链表有空则计算另一个链表,直到最后一个链表完成计算
     */
    while (curr1 != null || curr2 != null) { 
        int t = 0;
        if (curr1 == null) { //第一个链表运算结束
            t = curr2.val + up;
            curr2 = curr2.next;
        } else if (curr2 == null) { //第二个链表运算结束
            t = curr1.val + up;
            curr1 = curr1.next;
        } else { //两个链表运算都没有结束
            t = curr1.val + curr2.val + up;
            curr1 = curr1.next;
            curr2 = curr2.next;
        }
        up = t / 10;   //计算进位值(0,1)
        curr3.next = new ListNode(t % 10); //计算新节点的值大于10则取余
        curr3 = curr3.next;
    }
    //如果计算有进位值=1,则需要添加一个节点
    if (up == 1) {
        curr3.next = new ListNode(up);
    }
    return dummy.next;
}

两两交换链表中的节点

问题

力扣24\] [24. 两两交换链表中的节点 - 力扣(LeetCode)](https://leetcode.cn/problems/swap-nodes-in-pairs/description/)

问题描述

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

java 复制代码
输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

java 复制代码
输入:head = []
输出:[]

示例 3:

java 复制代码
输入:head = [1]
输出:[1]

解决方案

定义虚拟头结点dummy: dummy = new ListNode(-1,head);

定义临时节点tmp: tmp = dummy.next;

java 复制代码
ListNode dummy = new ListNode(-1, head);
ListNode temp = dummy;

while (temp.next != null && temp.next.next != null) {
    ListNode curr = temp.next; //当前节点
    ListNode next = temp.next.next; //当前节点的下一个节点
    //节点交换
    temp.next = next;
    curr.next = next.next;
    next.next = curr;
    temp = curr;
}
return dummy.next;

参考实现

java 复制代码
public ListNode swapPairs(ListNode head) {
    ListNode dummy = new ListNode(-1, head);
    ListNode temp = dummy;

    while (temp.next != null && temp.next.next != null) {
        ListNode curr = temp.next; //当前节点
        ListNode next = temp.next.next; //当前节点的下一个节点
        //节点交换
        temp.next = next;
        curr.next = next.next;
        next.next = curr;
        temp = curr;
    }
    return dummy.next;
}

对链表进行插入排序

问题

力扣147\] [147. 对链表进行插入排序 - 力扣(LeetCode)](https://leetcode.cn/problems/insertion-sort-list/description/)

问题描述

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头

插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

示例 1:

复制代码
输入: head = [4,2,1,3]
输出: [1,2,3,4]

示例 2:

复制代码
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]

解决方案

初始化

  1. 虚拟头结点dummy = new ListNode(-1,head);
  2. 记录排序后的后一个节点 sortedLast = dummy.next(先指向head位置): sortedLast记录插入点
  3. 待操作的节点curr = dummy.next.next。

如果sortedLast.val < curr.val, 准备进入循环移动小的节点

如果sortedLast.val > curr

从dummy开始查找比curr小的前一个节点位置

找到比curr小的位置,如图目前pre = dummy, 因为节点4的值大于节点2的值。

java 复制代码
sortedLast.next = curr.next; // curr小于sortedLast,那么curr即为sortedLast的新记录
curr.next = pre.next;
pre.next = curr;

移动curr索引

java 复制代码
  curr = sortedLast.next;

参考代码

java 复制代码
public ListNode insertionSortList(ListNode head) {

    if(head == null) return null;

    ListNode dummy = new ListNode(-1, head);
    ListNode sortedLast = dummy.next; // 排好序后的最后一个节点,类似于插入排序已排好序的最后一个位置值
    ListNode curr = dummy.next.next; // 待插入的节点

    while (curr != null) {
        // 当前待插入节点大于于排序序节点的最后一个节点,最后一个节点后移(tip: 已经不是最后一个了)
        if (sortedLast.val <= curr.val) {
            sortedLast = sortedLast.next;
        } else {// curr节点值小于排序好序的最后一个节点,需要向前查找合适位置(curr.val大于的位置)
            ListNode pre = dummy;
            while (pre.next.val <= curr.val) { // 移动pre找到比curr.val大的位置前一个位置
                pre = pre.next;
            }
            sortedLast.next = curr.next; // curr小于sortedLast,那么curr即为sortedLast的新记录
            curr.next = pre.next;
            pre.next = curr;
        }
        curr = sortedLast.next;
    }

    return dummy.next;
}

删除链表的倒数第N个节点

问题

力扣19\] [19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/)

问题描述

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

复制代码
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

复制代码
输入:head = [1], n = 1
输出:[]

示例 3:

复制代码
输入:head = [1,2], n = 1
输出:[1]

解决方案

虚拟头结点及快慢指针法
【初始化】

  • 虚拟头结点 : dummy = new ListNode(-1,head);
  • 快慢索引(指针): ListNode fast = dummy; ListNode slow= dummy;

让快指针(fast)先走n步。

java 复制代码
for (int i = n; i > 0; i--)
fast = fast.next;

两个指针(fast)和slow同时走到链表结尾。

如图所示,slow的末尾即与末尾的n个位置

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

参考代码

java 复制代码
public ListNode removeNthFromEnd(ListNode node, int n) {
ListNode pre = new ListNode(-1, node); // 定义要删除节点的前一个节点
// 定义快慢指针让其距离差n,让fast先走n个节点,fast与slow之间就差n
ListNode fast = pre;
ListNode slow = pre;
// slow ----------n------------- fast
for (int i = n; i > 0; i--)
    fast = fast.next;

// 两个节点同时移动,走到末尾,那么节点slow即为从末尾到n的节点
while (fast!=null &&fast.next != null) {
    fast = fast.next;
    slow = slow.next;
}

slow.next = slow.next.next;
return pre.next;
}

合并两个有序链表

题目

力扣21\] [21. 合并两个有序链表 - 力扣(LeetCode)](https://leetcode.cn/problems/merge-two-sorted-lists/description/)

题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

java 复制代码
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

java 复制代码
输入:l1 = [], l2 = []
输出:[]

示例 3:

java 复制代码
输入:l1 = [], l2 = [0]
输出:[0]

解决方案

拆除重组法

参考实现

java 复制代码
public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
    ListNode dummy = new ListNode(-1);
    ListNode curr1 = head1;
    ListNode curr2 = head2;
    ListNode curr = dummy;

    while (curr1 != null || curr2 != null) {
        if (curr1 == null) {
            curr.next = curr2;
            curr2 = curr2.next;
        } else if (curr2 == null) {
            curr.next = curr1;
            curr1 = curr1.next;
        } else if (curr1.val < curr2.val) {
            curr.next = curr1;
            curr1 = curr1.next;
        } else {
            curr.next = curr2;
            curr2 = curr2.next;
        }
        curr = curr.next;
    }
    return dummy.next;

}
java 复制代码
public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
    if (head1 == null) return head2;
    if (head2 == null) return head1;

    ListNode newNode = head1.val < head2.val ? head1 : head2;

    newNode.next = mergeTwoLists(newNode.next, head1.val >= head2.val ? head1 : head2);

    return newNode;

}

合并K个升序链表

题目

力扣23\] [23. 合并 K 个升序链表 - 力扣(LeetCode)](https://leetcode.cn/problems/merge-k-sorted-lists/description/?envType=problem-list-v2&envId=linked-list)

题目描述

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

java 复制代码
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

java 复制代码
输入:lists = []
输出:[]

示例 3:

java 复制代码
输入:lists = [[]]
输出:[]

解决方案

将每个节点的头结点放入优先队列,重新组装链表。

初始化

  • 优先链表定义
  • 虚拟头结点
java 复制代码
PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(o -> o.val));

ListNode dummy = new ListNode(-1);
ListNode curr = dummy;

将链表头节点放入优先队列(优先小队列)

java 复制代码
for (ListNode head : lists) {
    if(head !=null) priorityQueue.offer(head);
}

从优先队列中取出最小值,将头结点的下一个节点放入队列。

java 复制代码
while (!priorityQueue.isEmpty()) {
    ListNode node = priorityQueue.poll();
    curr.next = node;
    if (node.next != null) {
        priorityQueue.offer(node.next);
    }
    curr = curr.next;
}

参考解答

java 复制代码
public ListNode mergeKLists(ListNode[] lists) {

if(lists == null) return null;

PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(o -> o.val));

ListNode dummy = new ListNode(-1);
ListNode curr = dummy;

for (ListNode head : lists) {
    if(head !=null) priorityQueue.offer(head);
}

while (!priorityQueue.isEmpty()) {
    ListNode node = priorityQueue.poll();
    curr.next = node;
    if (node.next != null) {
        priorityQueue.offer(node.next);
    }
    curr = curr.next;
}
return dummy.next;
}

分割链表

题目

力扣86\] [86. 分隔链表 - 力扣(LeetCode)](https://leetcode.cn/problems/partition-list/description/?envType=problem-list-v2&envId=linked-list)

题目描述

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:

java 复制代码
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

java 复制代码
输入:head = [2,1], x = 2
输出:[1,2]

解决方案

java 复制代码
ListNode<Integer> dummyLess = new ListNode<>(-1);
ListNode<Integer> dummyLarge = new ListNode<>(-2);

ListNode<Integer> curr = head;
ListNode<Integer> currLess = dummyLess;
ListNode<Integer> currLarge = dummyLarge;

while (curr != null) {
    if (curr.val < x) {
        currLess.next = curr;
        currLess = currLess.next;

    } else {
        currLarge.next = curr;
        currLarge = currLarge.next;
    }
    curr = curr.next;
}

currLess.next = dummyLarge.next;
currLarge.next =null;
return dummyLess.next;
相关推荐
吧啦吧啦吡叭卜1 小时前
【打卡d5】快速排序 归并排序
java·算法·排序算法
大得3691 小时前
宝塔docker切换存储目录
java·docker·eureka
L_cl1 小时前
【Python 数据结构 15.哈希表】
数据结构·算法·散列表
东阳马生架构2 小时前
Netty基础—4.NIO的使用简介一
java·网络·netty
luckyext2 小时前
Postman用JSON格式数据发送POST请求及注意事项
java·前端·后端·测试工具·c#·json·postman
程序视点2 小时前
Redis集群机制及一个Redis架构演进实例
java·redis·后端
鱼樱前端2 小时前
Navicat17基础使用
java·后端
黑风风3 小时前
深入理解Spring Boot Starter及如何自定义Starter
java·spring boot·后端
px52133443 小时前
Solder leakage problems and improvement strategies in electronics manufacturing
java·前端·数据库·pcb工艺
Coder Zhang3 小时前
并查集,find函数的压缩路径,Union函数的小树合并大树
数据结构·算法