算法通关村第一关--链表经典问题之查找两个链表的第一个公共结点笔记

引言:以下内容均只是个人看法,并无严格经过核对,如有异议,欢迎交流,发现错误,欢迎您的及时指正,感谢

本文中使用的结点定义语句如下

public class listNode {
    public int data;
    public listNode next;
    public listNode(int data) {
        this.data = data;
        this.next=null;
    }
}

单链表的遍历一般从第一个结点依次遍历整个链表,所以第一个结点十分重要,在本文中头节点指的是第一个存放数据的结点,与其他相关文章中关于它的定义可能不同,无需深究,理解相关操作的逻辑原理即可,本质上无较大差别。

1.两个链表的第一个公共子节点

当存在两个单链表有公共结点时,可能出现如下图所示的情况,我们已经知道单链表中中间结点只能有一个后缀,对于其的前驱个数没有限制,所以下图满足单链表的要求。

对于题目没有思路怎么办?

这里的建议是把常用的数据结构和算法思想都想一遍,看看哪些可以解决问题。

常用的数据结构有数组,链表,队列,栈,HASH,集合,树,堆,常用的算法思想有查找,排序,双指针,递归,迭代,分治,贪心,回溯和动态规划等。

分析题目要求我们要找到的是两个单链中第一个公共点,且这个公共点在两个单链中的相对位置未知(即不知道在两个单链中第一个公共结点前面的结点个数)。

注意比较是否为公共结点时,如果在两个链表中某个结点的结点地址相同,即为公共结点

1.1蛮力法

首先我们可能想到的是蛮力法,既然要比较结点地址,那就从一条链表的第一个结点开始,去比较另一条链表的每一个结点,虽然简单,但是时间复杂度比较高,面试时排除,但是至少要知道如何实现。

代码如下

注意在while(node2!=null)前面要加node2=head2,以保证每次遍历第二条链表都从头遍历

    /**
     *
     * @param head1 第一条链表的头节点
     * @param head2 第二条链表的头节点
     * @return
     */
    public static listNode findFirstPublicNode(listNode head1,listNode head2){
        //用node1和node2代替head进行遍历链表
        listNode node1=head1;
        listNode node2=head2;
        //判断两个头节点是否为空,为空自然没有公共结点
        if(head1==null||head2==null){
            return null;
        }
        while(node1!=null){
            node2=head2;
            while(node2!=null){
                //对于引用数据类型,==表示对其地址的比较
                if(node1==node2){
                    return node1;
                }
                node2=node2.next;
            }
            node1=node1.next;
        }
        return null;
    }

2.HASH和集合法

基本思路:我们遍历一条链表并将其中元素存到Map中,之后遍历另一条链表并在Map中检测当前元素是否存在,第一次出现存在的情况对应的节点就是第一个公共节点,集合的思路与之类似。

利用HashMap的代码如下:

注意利用while遍历链表时,加上node指针后移的语句

    /**
     *
     * @param head1 第一条链表的头节点
     * @param head2 第二条链表的头节点
     * @return
     */
    public  static listNode findFirstPublicNode_hash(listNode head1,listNode head2){
        //创建一个HashMap用来存放一条链表中的节点
        HashMap<listNode,Integer> map=new HashMap<listNode, Integer>();
        //用node1和node2代替head进行遍历链表
        listNode node1=head1;
        listNode node2=head2;
        //判断两个头节点是否为空,为空自然没有公共结点
        if(head1==null||head2==null){
            return null;
        }
        while(node1!=null){
            //将第一条链表的结点存到Map
            map.put(node1,node1.data);
            node1=node1.next;
        }
        while(node2!=null){
            //判断map中是否可以检测到node2
            if(map.containsKey(node2)){
                return node2;
            }
            node2=node2.next;
        }
        return null;
    }

利用Set的代码如下

    /**
     *
     * @param head1 第一条链表的头节点
     * @param head2 第二条链表的头节点
     * @return
     */
    public  static listNode findFirstPublicNode_set(listNode head1,listNode head2){
        //新建一个集合
        Set<listNode> set=new HashSet<>();
        //用node1和node2代替head进行遍历链表
        listNode node1=head1;
        listNode node2=head2;
        //判断两个头节点是否为空,为空自然没有公共结点
        if(head1==null||head2==null){
            return null;
        }
        while(node1!=null){
            //将第一条链表的结点存到Map
            set.add(node1);
            node1=node1.next;
        }
        while(node2!=null){
            //判断map中是否可以检测到node2
            if(set.contains(node2)){
                return node2;
            }
            node2=node2.next;
        }
        return null;
    }

3.栈方法

基本思路:思考两个有公共结点的链表特点,查看本文开篇处的图片,我们发现两个链表的最后一段都是相同的结点,我们其实是找到这些里面的第一个,前面的方法都是正向遍历,

    /**
     *
     * @param head1 第一条链表的头节点
     * @param head2 第二条链表的头节点
     * @return
     */
    public  static listNode findFirstPublicNode_stack(listNode head1,listNode head2){
        Stack<listNode> stack1=new Stack<listNode>();
        Stack<listNode> stack2=new Stack<listNode>();
        //用node1和node2代替head进行遍历链表
        listNode node1=head1;
        listNode node2=head2;
        //判断两个头节点是否为空,为空自然没有公共结点
        if(head1==null||head2==null){
            return null;
        }
        while(node1!=null){
            stack1.push(node1);
            node1=node1.next;
        }
        while(node2!=null){
            stack2.push(node2);
            node2=node2.next;
        }
        listNode snode=null;
        while(stack1.size()>0&&stack2.size()>0){
            if(stack1.peek()==stack2.peek()){
                snode=stack1.pop();
                stack2.pop();
            }
            else {
                break;
            }
        }
        return snode;
    }

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值