链表(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;
    }
}

运行结果

相关推荐
吠品1 小时前
GitHub 周榜拆解:决策小模型扎堆上位
c语言·开发语言·算法
Ivanqhz1 小时前
MLA、DSA、CSA、HCA
人工智能·算法·机器学习
见叶之秋1 小时前
C++ 泛型世界的两块拼图:容器适配器与仿函数
java·开发语言
MayBaymax2 小时前
MQ 基础概念与架构
java·中间件·架构·java-rocketmq
Dovis(誓平步青云)2 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
嘎嘎风2 小时前
MiniMind 学习笔记之 07 模型怎么学会猜词(旋钮是怎么被拧动的)
算法·源码阅读
yuniko-n2 小时前
【Java】字符串相关坑点
java·开发语言·python
Terra.K2 小时前
Spring AI day1(SSE+AI)
java·人工智能·spring·springai
斑鸠喳喳2 小时前
线程本地存储 ThreadLocal
java·后端