首页 > 编程语言 >面试算法:获取重合列表的第一个相交节点

面试算法:获取重合列表的第一个相交节点

时间:2023-06-14 11:02:58浏览次数:50  
标签:Node 遍历 队列 T3 相交 列表 重合 节点


给定两个单向链表,这两个链表有可能会有重叠,情况如下:

面试算法:获取重合列表的第一个相交节点_面试

两个单向链表从节点5开始重合,要求给定一个空间复杂度为O(1)的算法,返回两个链表相交时的第一个节点。依据上图,也就是返回节点5.

首先我们需要做的是,确保给定的两个单向链表,他们是相交的。这个很好确定,只要从头遍历两个链表,如果他们的尾节点是一样的话,那么这两个链表就是相交的,问题是,如何尽快找到他们相交的第一个节点。

最笨的办法是,先找到尾节点,然后去掉尾节点,然后再次遍历查找新的尾节点,然后再去掉,直到两个链表没有共同的尾节点,那么最后去掉的共同尾巴节点,则是两个链表的首次相交节点。这种做法可行,但是算法复杂度是O(n^2)。有没有更好的办法呢?

假设第一个链表,从头结点到首次相交节点,所经历的距离用T1来表示,根据上图例子,T1 = 5, 也就是第一个队列从头结点0开始,需要经历节点1,2,3,4也就是总共5个节点后才能到达节点5.

我们用T3 表示队列2从头节点开始,到达首次相交节点的距离。根据上图,T3 = 3.

我们用T2表示两队列相交部分的节点数.依据上图T2 = 5.

由此队列1的长度为: T1 + T2 (1)
队列2的长度为:T3 + T2 (2)

如果我们能算出T3的数值,那么我们从队列2的头结点出发,经过T3-1步后,就能达到首次相交节点。我们如何计算T3的数值呢?

对T3的计算,需要一个小技巧.我们把队列2进行反转,得到下面情形:

面试算法:获取重合列表的第一个相交节点_算法_02

如果此时我们从队列1的头结点开始进行遍历,那么从上头的节点0开始出发,会到队列2的头结点0结束。这样,在反转后,如果再次从头遍历队列1的话,得到的长度就是:

T1 + T3 + 1 (3).

根据上面三个公式,我们便可以计算出T3来。

(3) - (1) = T1 + T3 + 1 - T1 - T2 = T3 - T2 + 1
(3) - (1) + (2) = T3 - T2 + 1 + T3 + T2 = 2*T3 + 1

由此,我们可以反解出T3, 有了T3,我们便可以得到两队列首次相交节点了。

这个算法除了需要遍历两个队列外,还需要对其中一个队列进行反转,无论是遍历还是反转,其算法复杂度都是O(n), 因此总算法复杂度是O(n).

代码实现:

public class ListIntersetChecker {
    private Node listHead1;
    private Node listHead2;
    private int firstListLen = 0;  //t1 + t2
    private int secondListLen = 0; // t3 + t2
    private int lenAfterReverse = 0; // t1 + t3
    private ListReverse listReverse = null;

    public ListIntersetChecker(Node listHead1, Node listHead2) {
        this.listHead1 = listHead1;
        this.listHead2 = listHead2;

    }

    public Node getFirstIntersetNode() {
        if (isTwoListInterset() == false) {
            return null;
        }

        listReverse = new ListReverse(listHead2);

        Node reverseHead = listReverse.getReverseList();
        lenAfterReverse = getListLen(listHead1);

        int t3 = ((lenAfterReverse - firstListLen) + secondListLen - 1) / 2;
        int steps = secondListLen - t3 - 1;
        while (steps > 0) {
            reverseHead = reverseHead.next;
            steps--;
        }

        return reverseHead;
    }

    private int getListLen(Node head) {
        int len = 0;
        while (head != null) {
            head = head.next;
            len++;
        }

        return len;
    }

    private boolean isTwoListInterset() {
        Node head1 = listHead1;
        Node head2 = listHead2;

        while (head1.next != null || head2.next != null) {
            if (head1.next != null) {
                head1 = head1.next;
                firstListLen++; 
            }

            if (head2.next != null) {
                head2 = head2.next;
                secondListLen++;
            }

        }

        firstListLen++;
        secondListLen++;

        return head1 == head2;
    }


}

ListIntersetCheck.java 用于实现上面描述的算法。 getFirstIntersetNode返回两重叠队列首次相交节点。isTwoListInterset 用于判断两队列是否相交。在遍历两队列时,统计两队列的长度,也就是获得 T1 + T2 以及 T3 + T2的值。

然后把队列2进行反转,反转后,再从队列1的头节点进行遍历,得到的lenAfterReverse就是 T1 + T3 + 1.

int t3 = ((lenAfterReverse - firstListLen) + secondListLen - 1) / 2;
上面语句则根据前面的推导计算出T3.

由于队列2已经反转了,所以不能从队列2的头结点去遍历,只能从队列2的尾节点开始遍历,如果头结点开始遍历需要T3步的话,那么从尾节点遍历,则需要steps = secondListLen - (T3 + 1) 步。

由此,代码从队列2反转后的头结点开始,经过steps个节点后抵达两队列首次相交时的节点。

再看看主入口代码:

public class LinkList {
    public static void main(String[] args) {
        ListUtility util1 = new ListUtility();
        ListUtility util2 = new ListUtility();

        Node list1 = util1.createList(10);
        Node list2 = util2.createList(3);

        Node node = util1.getNodeByIdx(5);
        Node tail = util2.getNodeByIdx(2);
        tail.next = node;

        ListIntersetChecker intersetChecker = new ListIntersetChecker(list1, list2);
        Node interset = intersetChecker.getFirstIntersetNode();
        System.out.println("The first interset node is : " + interset.val);
    }
}

程序启动时,先构造两个队列,队列1节点从0到9,队列2从0到2,然后把队列2的尾节点的next指向队列1的编号为5的节点,于是就构造了我们例子图中的两个相交队列,然后再利用ListIntersetChecker获得两重合队列的首个相交节点。

最后程序运行结果为:
The first interset node is : 5
结果跟我们理论推导一致,也就是说,我们的说法实现是正确的。更详细的代码讲解和推导调试过程,请参看视频。

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:

面试算法:获取重合列表的第一个相交节点_面试_03


标签:Node,遍历,队列,T3,相交,列表,重合,节点
From: https://blog.51cto.com/u_16160261/6476235

相关文章

  • 算法面试之道:在O(1)的时间内删除单链接链表的指定节点
    对于一个单项链接的链表,给定其中某个任意节点,要求在O(1)的时间复杂度内删除该节点。表面上看起来,似乎不可能做到,因为如果要求时间复杂度是O(1)的话,那意味着,算法实现中,不得包含有任何循环或是对链表的整体遍历。但问题在于,要删除某个指定节点,我们需要通过遍历,找到该节点的前节点,然......
  • 2023年 1月 Tita 升级|绩效流程节点支持签名确认
    升级快速一览:点击免费领取绩效考核模版等资料·【考核模板】绩效流程节点支持签名确认;绩效流程节点支持签名确认使用场景:考核执行过程中,在指标确认、绩效面谈、绩效结果确认节点需要执行人的签名确认,并将签名进行留存当前仅支持在指标确认、绩效面谈、绩效结果确认节......
  • 收藏文章列表
    一、MySQL相关1、ONDUPLICATEKEYUPDATE用法与说明ONDUPLICATEKEYUPDATE用法与说明二、Mybatis1、Mybatis简介Mybatis简介https://mybatis.org/mybatis-3/zh/index.html三、Python学习地址黑马程序员Python小白基础入门教程Python入门到精通教程黑马程序员全套Python教程_P......
  • fastadmin把后端变量传递到指定列表下的js文件
    php文件$this->assignconfig("customer_status_list",DictionaryService::getCustomerFieldDictionaryConfig('customer_status'));js文件{field:'customer_status',title:'客户状态',operate:"LIKE",......
  • java 获取ftp文件列表以及模糊查询,并对结果进行分页
    /***获取ftp文件列表*".*\\.txt":匹配所有以".txt"结尾的文件名。其中,星号(*)表示任意字符序列,反斜杠(\)用于转义点号(.)字符。*".*"+"任意字符"+".*\\.txt":匹配所有包含"表示匹配任意多个任意字符"和以".txt"结尾的文件名。其中,星号(*)表示任意字......
  • 1483. 树节点的第 K 个祖先 又学了一招
    1483.树节点的第K个祖先最开始自己的思路竟然是想构建多叉树,这样实在太蠢了,还是直接学习优秀的官解吧。学习历程暴力解法学习举例说明:n=7parent=[-1,0,0,1,1,2,2]一共七个节点节点的值分别是:0,1,2,3,4,5,6-1:表示根节点ex:寻找节点6的祖先节点第一个祖先节点:parent[6]=2......
  • Camunda 自定义模型图后 流程节点叠在一起怎么查看
    简单写一下 后面详细补充   根据这个sql语句可以把乱码的数据转码过来SELECTcast(BYTES_ASCHAR)ASBYTES_FROM`act_ge_bytearray`WHEREID_='e4e532d0-c146-11ec-b630-18f22c5016b2'; 把转码后的xml放到CamundaModeler客户端编辑器里面去  ......
  • LC1171. 从链表中删去总和值为零的连续节点
    题目来源于力扣题库,题目链接:LC1171.从链表中删去总和值为零的连续节点Q:给你一个链表的头节点 head,请你编写代码,反复删去链表中由总和 值为0的连续节点组成的序列,直到不存在这样的序列为止。删除完毕后,请你返回最终结果链表的头节点。 你可以返回任何满足题目要求的答案。......
  • Redis集群-哨兵模式搭建(1主2从3哨兵节点)
    Redis集群-哨兵模式搭建(1主2从3哨兵节点)原创 北极星 运维记事 2023-04-2022:47 发表于四川收录于合集#redis8个主机规划类型IP地址端口号主192.168.77.1456379从1192.168.77.1466379从2192.168.77.1476379哨兵1192.168.77.14526379哨兵2......
  • 2023.6.12 树节点的第k个祖先
    可以借鉴一下求LCA问题中的倍增思想。用fa[i][j]表示i号节点的第\(2^j\)个祖先。我们只需要用动态规划预处理出这个fa数组即可。求第k个祖先,可以将k用二进制拼凑的方法划分成若干个2的整数次幂,然后利用fa数组对应地进行若干次跳跃即可,单个询问的时间复杂度\(O(logn)\)。这里由......