数据结构与算法---链表---相交链表、分隔链表

本文介绍了两种寻找两个单链表相交起始节点的方法。一种是通过调整较短链表来匹配较长链表的长度,另一种是在遍历到链表末端时转向另一个链表头部继续遍历。

相交链表

160. 相交链表

编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在这里插入图片描述在节点 c1 开始相交。

例如:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

注意:

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

思路1

要是能倒推就简单了
5的前一个指针是4
4的前一个指针是8
8的前一个指针有两个:1和1
说明,8是相交结点

问题是,单向链表能找到前一个结点吗?
那就涉及到翻转链表

正着推
4->1->8->4->5
5->0->1->8->4->5

4跟5
1跟0
8跟1
4跟8
5跟5
null跟5
都不一样,两个链表成了不相交了。这,肯定不对。

思路2

两个链表,找出长度比较短的那个,在前面补-1,使得短的链表长度最后和长的链表长度一样
-1->4->1->8->4->5
5->0->1->8->4->5

然后,再比较

既然是新添加的-1,肯定跟对应的不相等,可直接略过,从不是新添加的地方开始比较。

也就是,直接比较
4->1->8->4->5
0->1->8->4->5

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //如果两个链表有一个是空,则返回null
        if(headA == null || headB == null) return null;

        //由于需要保证原结构不变,需要定义新的结点进行操作
        ListNode currentNodeHeadA = headA;
        ListNode currentNodeHeadB = headB;

        //求出链表A的长度
        int headALength = 0;
        while(currentNodeHeadA != null)
        {
            currentNodeHeadA = currentNodeHeadA.next;
            headALength++;
        }
        //求出链表B的长度
        int headBLength = 0;
        while(currentNodeHeadB != null)
        {
            currentNodeHeadB = currentNodeHeadB.next;
            headBLength++;
        }

        //恢复位置
        currentNodeHeadA = headA;
        currentNodeHeadB = headB;
        
        if(headALength > headBLength)
        {
            //A从headALength - headBLength开始
            //B从头开始
            for(int i = 0; i<headALength - headBLength; i++)
            {
                currentNodeHeadA = currentNodeHeadA.next;
            }
        }else{
            //B从headBLength - headALength开始
            //A从头开始
            for(int i = 0; i<headBLength - headALength; i++)
            {
                currentNodeHeadB = currentNodeHeadB.next;
            }
        }

        while(currentNodeHeadA != null && currentNodeHeadB != null)
        {
            if(currentNodeHeadA != currentNodeHeadB)
            {
                currentNodeHeadA = currentNodeHeadA.next;
                currentNodeHeadB = currentNodeHeadB.next;
            }else{
                return currentNodeHeadA;
            }
        }
        return null;
    }
}

MJ是更秀

思路3

在这里插入图片描述
当判断A链表的某一个节点为null的时候,将B链表拼接到A链表的最后面
同理对B
需要注意的是,如果两个链表不相交,则可能会循环调用。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        
        //如果两个链表有一个是空,则返回null
        if(headA == null || headB == null) return null;
        ListNode currentNodeHeadA = headA;
        ListNode currentNodeHeadB = headB;

        while(currentNodeHeadA != currentNodeHeadB){
            currentNodeHeadA = (currentNodeHeadA == null) ? headB : currentNodeHeadA.next;
            currentNodeHeadB = (currentNodeHeadB == null) ? headA : currentNodeHeadB.next;
        }
        return currentNodeHeadA;
    }
}

两个地方需要注意:

错误
currentNodeHeadA = (currentNodeHeadA == null) ? currentNodeHeadB: currentNodeHeadA.next;
currentNodeHeadB = (currentNodeHeadB == null) ? currentNodeHeadA: currentNodeHeadB.next;

拼接的时候,需要拼接是原有的headA或headB

错误
currentNodeHeadA = (currentNodeHeadA.next == null) ? headB : currentNodeHeadA.next;
currentNodeHeadB = (currentNodeHeadB.next == null) ? headA : currentNodeHeadB.next;

使用currentNodeHeadA.next,当两个链表不相交的时候,会循环调用。


分隔链表

86.分隔链表

题目

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

思路

如果是分隔成两部分,则将结果看成是由两个链表构成,1->2->2 和 4->3->5
然后将两个链表合成一个链表

分隔成三部分,就使用三个链表。

使用链表A,找出比3小的结点
使用链表B,找出比3大或等的结点
之后,将B放在A的末尾
需要注意的是,B的结尾需要手动变为null

在这里插入图片描述

class Solution {
    public ListNode partition(ListNode head, int x) {
        if(head == null) return null;

        ListNode leftDummyHead = new ListNode(-1);
        ListNode rightDummyHead = new ListNode(-1);

        ListNode leftCurrentNode = leftDummyHead;
        ListNode rightCurrentNode = rightDummyHead;

        while(head != null){
            if(head.val < x){
                leftCurrentNode.next = head;
                leftCurrentNode = leftCurrentNode.next;
            }else{
                rightCurrentNode.next = head;
                rightCurrentNode = rightCurrentNode.next;
            }
            head = head.next;
        }

        //需要注意的地方
        rightCurrentNode.next = null;
        
        //拼接
        leftCurrentNode.next = rightDummyHead.next;

        return leftDummyHead.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值