当前位置: 首页 > article >正文

Day8 25/2/21 FRI

【一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解(马士兵)】https://www.bilibili.com/video/BV13g41157hK?p=4&vd_source=04ee94ad3f2168d7d5252c857a2bf358

目录

4、链表

4.3.4 例4 两个单链表相交

解法1:存set

解法2:快慢指针

情况1:都无环

(1-1)哈希表。

(1-2)仅用有限几个变量

情况2:一个有环一个无环

情况3:两个都有环

主函数的调用


笔记:

4、链表

4.3.4 例4 两个单链表相交

题目:给定两个可能有环也可能无环的单链表,头节点head1和head2。实现一个函数,如果两链表香蕉则返回相交的第一个节点;如果不相交则返回null。

【要求】如果量链表长度之和为N,时间复杂度要达到O(N)、额外空间复杂度要达到O(1)。

两链表相交,即两表各自有一个节点的内存地址相同。

解法1:存set

遍历链表然后把节点存入set,在set李第二次遇到相同节点时,该节点就是入环的第一个节点

解法2:快慢指针

这个方法无需调用额外的数据结构。

单链表如果有环的话,不会出现下图的状况。因为如果有环且环之后还留有拖尾,则会导致交点处有两个next指针,而这在单链中不会存在。

如果单链中有环,只会如下图所示。

遍历单链时,一旦最终走到了null,则说明没有环。有环的话最终一定走到入环的第一个节点处。

首先,快指针F每次走两步、慢指针S每次走一步,如果有环的话它俩必然在环内某个节点相遇。

然后慢指针S停留在原地,快指针F返回单链的开头,两个指针每次都各走一步,它们最终一定会在入环的第一个节点相遇。

解释:F走的步数是S的两倍,F与S的步差值是环的n倍,所以F和S都走了环的整数倍。再走环外的步数都会走到环起始点,因此相遇。

public static Node getLoopNode(Node head){
    if(head == null || head.next == null || head.mext.next == null{
        return null;
    }
    Node slowP = head.next;  //慢指针一次走一格
    Node fastP = head.next.next;  //快指针一次走两格
    while( slowP != fastP){
        if(fastP.next == null || fastP.next.next = null){
            return null;  //也就是单链上无环
        }

        fastP = fastP.next.next;
        slowP = slowP.next;
    }
    fastP = head;  //代码走到这步,说明单链上有环,于是快指针返回到单链的头,然后继续找入环的第一个节点
    while(fastP != slowP){
        fastP = fastP.next;
        slowP = slowP.next;
    }
    return slowP;
}

现在继续看两个链表的相交问题。

用上面的函数分别运行两个单链表,得到的入环节点的返回值可能有:

情况1:都无环

loop1 == null 且 loop2 == null

解释:两条单链不相交。由于各自都是单链,节点到节点之间只有一个next指针,所以不可能出现下图的情况。下图的相交节点处会出现往左和往右的两个next指针,因此不成立。

如果两条单链相交且没有环,则说明两条单链交点之后的部分一定是公共部分,类似下图。

找到情况1的相交节点,有两种方法。

(1-1)哈希表。

用哈希表记录一条链的节点,然后边遍历另一条链边检查节点是否在哈希表中。

(1-2)仅用有限几个变量

用需要分别从头到尾遍历单链1和单链2,并各自记录链的长度len1、len2(也可以像下面代码中用一个变量len,遍历单链1是len++,遍历单链2时len—)。然后让链更长的那条先走| len1-len2 |步,然后另一条从头开始走,这样它们必然在第一个相交节点处相遇,如同对齐然后拉上拉链一样。

public static Node noLoop(Node head1, Node head2){
    if(head1 == null || head2 == null){
        return null;
    }
    Node cur1 = head1;
    Node cur2 = head2;
    int len = 0;
    while(cur1.next != null){
        len++;   //遍历计算单链1的长度
        cur1 = cur1.next;
    }
    while(cur2.next != null){
        len--;   //遍历完单链2之后,len的数值就是单链1和2的长度差值
        cur2 = cur2.next;
    }
    if( cur1 != cur2) return null;  //两个单链最终没有汇合在同一个尾部的情况(需要排除)
    if( n > 0 ){ //单链1长度>单链2
        cur1 = head1;   //此时cur1等于更长的那条链的头部
        cur2 = head2
    }else{  //单链1长度<=单链2
        cur1 = head2;    //此时cur1等于更长的那条链的头部
        cur2 = head1;
    }
    n = Math.abs(n);  //让长的那条链先走完n步
    while( n !=0 ){
        n--;
        cur1 = cur1.next;
    }
    while( cur1 !=cur2 ){  //两条里一起遍历,找到第一个相交节点
        cur1 = cur1.next;
        cur2 = cur2.next; 
    }
    return cur1;
}
情况2:一个有环一个无环

两单链此时不可能相交。

情况3:两个都有环

loop1 != null且loop2 != null

情况3共有三种不同处境:

(3-1)不相交且入环节点也不同;(3-2)相交且交点在环之前;(3-3)相交且入环节点不同

                                                    

对于(3-2)的情况,可参考上面(1-2)的相交且无环的代码。

区分(3-1)和(3-3),只需要让一个单链1走入环后停止并记录当前节点,单链2在环里继续走,看在环中转的时候单链2是否能遇到单链1的那个节点即可。

能遇到则是(3-3),返回单链1或单链2的第一个入环节点都可以。遇不到则是(3-1),第一个入环节点为null。

public static void bothLoop(Node head1, Node loop1, Node head2, Node loop2){
    Node cur1 = null;
    Node cur2 = null;
    if(loop1 == loop2){  //这部分就是(1-2)的代码
        cur1 = head1;
        cur2 = head2;
        int len = 0;
        while(cur1 != loop1){
            len++;   //遍历计算单链1的长度
            cur1 = cur1.next;
        }
        while(cur2 != lopp2){
            len--;   //遍历完单链2之后,len的数值就是单链1和2的长度差值
            cur2 = cur2.next;
        }
        if( n > 0 ){ //单链1长度>单链2
            cur1 = head1;   //此时cur1等于更长的那条链的头部
            cur2 = head2;
        }else{  //单链1长度<=单链2
            cur1 = head2;    //此时cur1等于更长的那条链的头部
            cur2 = head1;
        }
        n = Math.abs(n);  //让长的那条链先走完n步
        while( n !=0 ){
            n--;
            cur1 = cur1.next;
        }
        while( cur1 !=cur2 ){  //两条里一起遍历,找到第一个相交节点
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
    }else{
        cur1 = loop.next;
        while(cur1 != loop1){  //cur1一直走,在回到loop1这个节点之前如果遇到了loop2,就是(3-3)的情况,否则就是(3-1)的情况
            if(cur1 == loop2){
                return loop1;
            }
            cur1 = cur1.next
        }
    return null;
    }
}
主函数的调用
public static Node getIntersectNode(Node head1, Node head2){
    if(head1 == null || head2 == null){
        return null;
    }
    Node loop1 = getLoopNode(head1);     //getLoopNode():找到链表第一个入环节点,如果无环则返回null
    Node loop2 = getLoopNode(head2);
    if(loop1 == null && loop2 == null){  //情况1
        return noLoop(head1, head2);
    }
    if(loop1 != null && loop2 != null){  //情况3
        return bothLoop(head1, loop1, head2, loop2);
    }
    return null;  //情况2
}


http://www.kler.cn/a/556436.html

相关文章:

  • 破局与重构:水务企业数字化转型路径探索
  • 解决elementUi el-select 响应式不生效的问题
  • 使用 pjsua2 开发呼叫机器人,批量拨打号码并播放固定音频
  • Windows 下免费开源的多格式文件差异对比工具
  • 什么是逻辑分析仪?
  • 【C# 数据结构】队列 FIFO
  • HTML 中的 Canvas 样式设置全解
  • 【deepseek】Ubuntu/centos系统中无法直接git clone下载模型的解决方法(手动下载)
  • js面试八股
  • ESP32 websocket-client
  • DuodooBMS源码解读之 purchase_change 模块
  • ABAP数据库表的增改查
  • 深入理解 SQL 注入漏洞及解决方案
  • QTextEdit达到指定行数自动清理+光标移动到末端(QT/C++)
  • 【CXX】4.1 CXX与Cargo集成配置详解
  • DeepSeek04-导出导入模型文件
  • Bootstrap Blazor UI 中 <Table> 组件 <TableColumn> 使用备忘01:EF Core 外码处理
  • Could not download npm for node v14.21.3(nvm无法下载节点v14.21.3的npm)
  • SeaTunnel社区「Demo方舟计划」首期活动上线—— MySQL CDC实时同步至PostgreSQL实战
  • Android 底层判断/dev/video节点是否是可用摄像头