链表(4)

一、两个链表的第一个公共结点

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围: n≤1000

要求:空间复杂度 O(1),时间复杂度 O(n)

输入:

输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。

返回值:

返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。

复制代码
import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        // 逻辑上,将 pHead2 "接到" pHead1 后面,将 pHead1 "接到" pHead2 后面
        // 然后让两个指针分别遍历两个链表,二者步伐保持一致,最终终将在第一个公共节点相遇
        ListNode p1 = pHead1, p2 = pHead2;
        while (p1 != p2) {
            p1 = p1 == null ? pHead2 : p1.next;
            p2 = p2 == null ? pHead1 : p2.next;
        }
        return p1;
    }
}

运行结果

二、链表相加

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。

给定两个这种链表,请生成代表两个整数相加值的结果链表。

数据范围:0≤n,m≤1000000,链表任意值 0≤val≤9

要求:空间复杂度 O(n),时间复杂度 O(n)

复制代码
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {

    public ListNode addInList(ListNode head1, ListNode head2) {
        // write code here
        Deque<ListNode> stack1 = listToStack(head1);
        Deque<ListNode> stack2 = listToStack(head2);
        ListNode dummy = new ListNode(-1);
        int carry = 0;
        while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
            int v1, v2;
            if (!stack1.isEmpty()) {
                v1 = stack1.pop().val;
            } else {
                v1 = 0;
            }
            if (!stack2.isEmpty()) {
                v2 = stack2.pop().val;
            } else {
                v2 = 0;
            }
            int total = v1 + v2 + carry;
            carry = total / 10;
            ListNode newNode = new ListNode(total % 10);
            // 头插
            newNode.next = dummy.next;
            dummy.next = newNode;
        }
        return dummy.next;
    }

    private Deque<ListNode> listToStack(ListNode head) {
        ListNode cur = head;
        Deque<ListNode> stack = new ArrayDeque<>();
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        return stack;
    }
}

运行结果

三、单链表的排序

给定一个节点数为n的无序单链表,对其按升序排序。

数据范围:0<n≤100000。

要求:空间复杂度 O(n),时间复杂度 O(nlogn)

复制代码
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {

    public ListNode sortInList(ListNode head) {
        // 空链表和只有一个节点的链表无需排序
        if (head == null || head.next == null) {
            return head;
        }
        // 找到切分点,从该点开始将链表一分为二
        // 从该节点后面将链表一分为二。
        ListNode splitPoint = findSplitPoint(head);
        ListNode left = head, right = splitPoint.next;
        splitPoint.next = null;
        // 合并两个有序数组
        return merge(sortInList(left), sortInList(right));
    }

    /**
     * 最开始认为切分点应该是链表的中间节点。
     * 实际运行时,遇到了栈溢出错误,说明这里发生了无限递归。
     * 后续在 Debug 的过程中注意到如下的问题:
     * 当链表长度为 2 时,中间节点就是第 2 个节点。
     * 从它后面切开后 right 为空,left 仍然是原链表!
     * 这就是导致无限递归的根本原因!
     * 从长度为偶数的链表出发,注意到中间节点实际上属于右半部分。
     * 所以切分点应该是其前一个节点。
     * 这样从切分点后面切开,才能得到两个规模都严格小于原链表的子链表,从而保证递归能够最终到达终止条件。
     */
    private ListNode findSplitPoint(ListNode head) {
        // 让 fast 比 slow 先走一步,使 slow 最终停在合适的切分点
        ListNode fast = head.next, slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

    private ListNode merge(ListNode head1, ListNode head2) {
        ListNode dummy = new ListNode(-1), k = dummy;
        while (head1 != null && head2 != null) {
            if (head1.val < head2.val) {
                k.next = head1;
                head1 = head1.next;
            } else {
                k.next = head2;
                head2 = head2.next;
            }
            k = k.next;
        }
        k.next = head1 == null ? head2 : head1;
        return dummy.next;
    }
}

运行结果

相关推荐
SimonKing1 小时前
又找到一个纯Java的OCR项目,识别的格式还挺多
java·后端·程序员
lengxuenong2 小时前
区间覆盖问题 · 简明学习手册1
java·学习·算法
?abc!2 小时前
(贪心)621. 任务调度器
java·前端·c#
jason成都2 小时前
rtklib_java PPP-RTK 收官测试:修复 BDS-2 宽巷失效与异常卫星死循环 Bug
java·gnss·rtklib·rtklib_java
明志数科2 小时前
人类视频与机器人示教的数据配比:分层训练的数据采购与采集设计
人工智能·算法
Flynn Liang2 小时前
Spring AI ChatClient 详解:system/user/assistant 角色、call 与 stream 到底怎么选
java·人工智能·spring·springai·流式输出·chatclient·java接入大模型
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 喜欢与取消喜欢
java·开发语言·交友
Ivanqhz2 小时前
MoE 原理、TC/EC 路由、LSH-MoE 与负载均衡浅析
运维·网络·人工智能·深度学习·算法·机器学习·负载均衡
Sarvartha2 小时前
栈的四则运算、栈的递归应用
数据结构·算法
摸鱼仙人~2 小时前
校招帮简历字段匹配架构分析报告
java·前端·架构