1. 题目

描述

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围:n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)

例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:

可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。

输入描述:

输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。

返回值描述:

返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。

示例1

输入:

{1,2,3},{4,5},{6,7}

返回值:

{6,7}

说明:

第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的          

示例2

输入:

{1},{2,3},{}

返回值:

{}

说明:

2个链表没有公共节点 ,返回null,后台打印{}   

2. 解题思路

获取两链表的第一个公共节点可以采用如下步骤完成:

步骤一:定义两个指针变量,分别指向两个链表的头节点。

步骤二:移动两个指针变量。

  • 移动指针变量h1,如果指向节点为空:更改指向到另外一个链表(更改到链表2);
  • 移动指针变量h2,如果指向节点为空:更改指向到另外一个链表(更改到链表1)。

指针变量 h1移动轨迹如下:

指针变量 h2移动轨迹如下:

在此过程h1与h2同时移动,移动的前提条件是:h1!=h2。移动示意图如下所示:

步骤三:返回公共节点。

两指针h1与h2相遇, 即:h1与h2指向相同的节点。如果没有公共节点,则:h1与h2都指向Null。

如果文字描述的不太清楚,你可以参考视频的详细讲解。

3. 编码实现

3.1 Python编码实现

class ListNode:
    def __init__(self, x):
        self.val = x  # 链表的数值域
        self.next = None  # 链表的指针域


# 从链表节点尾部添加节点
def insert_node(node, value):
    if node is None:
        print("node is None")
        return
    # 创建一个新节点
    new_node = ListNode(value)
    cur = node
    # 找到链表的末尾节点
    while cur.next is not None:
        cur = cur.next
    # 末尾节点的next指针域连接新节点
    cur.next = new_node


# 打印链表(从链表头结点开始打印链表的值)
def print_node(node):
    cur = node
    # 遍历每一个节点
    while cur is not None:
        print(cur.val, end="\t")
        cur = cur.next  # 更改指针变量的指向
    print()


#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param pHead1 ListNode类
# @param pHead2 ListNode类
# @return ListNode类
#
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        # 1. 定义两个指针变量,分别指向两个链表的头节点
        h1 = pHead1
        h2 = pHead2

        # 2. 移动两个指针变量
        while h1 is not h2:
            # 2.1 移动指针变量h1,如果指向节点为空:更改指向到另外一个链表(更改到pHead2链表)
            if h1 is None:
                h1 = pHead2
            else:
                h1 = h1.next

            # 2.2 移动指针变量h2,如果指向节点为空:更改指向到另外一个链表(更改到pHead1链表)
            if h2 is None:
                h2 = pHead1
            else:
                h2 = h2.next

        # 3. 返回公共节点(两指针相遇 即:两链表指向相同的节点。如果没有公共节点,则:两链表都指向None)
        return h2


if __name__ == '__main__':
    comm_nodes = ListNode(6)
    comm_nodes.next = ListNode(7)

    root1 = ListNode(1)
    root1.next = ListNode(2)
    root1.next.next = ListNode(3)
    root1.next.next.next = comm_nodes
    print_node(root1)

    root2 = ListNode(4)
    root2.next = ListNode(5)
    root2.next.next = comm_nodes
    print_node(root2)

    s = Solution()
    node = s.FindFirstCommonNode(root1, root2)
    print(node.val)
    print_node(node)

    print("+++++++++++++++++++++++++++++++++++++++++")
    head1 = ListNode(1)
    head2 = ListNode(2)
    head2.next = ListNode(3)
    node1 = s.FindFirstCommonNode(head1, head2)
    print(node1)

3.2 Java编码实现

package LL10;


public class Main {
    //定义链表节点
    static class ListNode {
        private int val;  //链表的数值域
        private ListNode next; //链表的指针域

        public ListNode(int data) {
            this.val = data;
            this.next = null;
        }
    }

    //添加链表节点
    private static void insertNode(ListNode node, int data) {
        if (node == null) {
            return;
        }
        //创建一个新节点
        ListNode newNode = new ListNode(data);
        ListNode cur = node;
        //找到链表的末尾节点
        while (cur.next != null) {
            cur = cur.next;
        }
        //末尾节点的next指针域连接新节点
        cur.next = newNode;
    }

    //打印链表(从头节点开始打印链表的每一个节点)
    private static void printNode(ListNode node) {
        ListNode cur = node;
        //遍历每一个节点
        while (cur != null) {
            System.out.print(cur.val + "\t");
            cur = cur.next; //更改指针变量的指向
        }
        System.out.println();
    }


    public static class Solution {
        /**
         * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
         *
         * @param pHead1 ListNode类
         * @param pHead2 ListNode类
         * @return ListNode类
         */
        public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
            // write code here
            // 1. 定义两个指针变量,分别指向两个链表的头节点
            ListNode h1 = pHead1;
            ListNode h2 = pHead2;

            // 2. 移动两个指针变量
            while (h1 != h2) {
                // 2.1 移动指针变量h1,如果指向节点为空:更改指向到另外一个链表(更改到pHead2链表)
                if (h1 == null) {
                    h1 = pHead2;
                } else {
                    h1 = h1.next;
                }

                // 2.2 移动指针变量h2,如果指向节点为空:更改指向到另外一个链表(更改到pHead1链表)
                if (h2 == null) {
                    h2 = pHead1;
                } else {
                    h2 = h2.next;
                }
            }
            // 3. 返回公共节点(两指针相遇 即:两链表指向相同的节点。如果没有公共节点,则:两链表都指向null)
            return h1;
        }
    }

    public static void main(String[] args) {
        ListNode commNodes = new ListNode(6);
        commNodes.next = new ListNode(7);

        ListNode root1 = new ListNode(1);
        root1.next = new ListNode(2);
        root1.next.next = new ListNode(3);
        root1.next.next.next = commNodes;
        printNode(root1);

        ListNode root2 = new ListNode(4);
        root2.next = new ListNode(5);
        root2.next.next = commNodes;
        printNode(root2);

        Solution solution = new Solution();
        ListNode node = solution.FindFirstCommonNode(root1, root2);
        System.out.println(node.val);
        printNode(node);

        System.out.println("+++++++++++++++++++++++++++++++++++++++++");
        ListNode head1 = new ListNode(1);
        ListNode head2 = new ListNode(2);
        head2.next = new ListNode(3);
        ListNode node1 = solution.FindFirstCommonNode(head1, head2);
        System.out.println(node1);
    }
}

3.3 Golang编码实现

package main

import "fmt"

// ListNode 定义链表节点
type ListNode struct {
    Val  int       //链表的数值域
    Next *ListNode //链表的指针域
}

/**
 *
 * @param pHead1 ListNode类
 * @param pHead2 ListNode类
 * @return ListNode类
 */
func FindFirstCommonNode(pHead1 *ListNode, pHead2 *ListNode) *ListNode {
    // write code here
    // 1. 定义两个指针变量,分别指向两个链表的头节点
    h1 := pHead1
    h2 := pHead2

    // 2. 移动两个指针变量
    for h1 != h2 {
        // 2.1 移动指针变量h1,如果指向节点为空:更改指向到另外一个链表(更改到pHead2链表)
        if h1 == nil {
            h1 = pHead2
        } else {
            h1 = h1.Next
        }

        // 2.2 移动指针变量h2,如果指向节点为空:更改指向到另外一个链表(更改到pHead1链表)
        if h2 == nil {
            h2 = pHead1
        } else {
            h2 = h2.Next
        }

    }
    // 3. 返回公共节点(两指针相遇 即:两链表指向相同的节点。如果没有公共节点,则:两链表都指向nil)
    return h2

}
func main() {
    comNodes := &ListNode{Val: 6}
    comNodes.Next = &ListNode{Val: 7}

    root1 := &ListNode{Val: 1}
    root1.Next = &ListNode{Val: 2}
    root1.Next.Next = &ListNode{Val: 3}
    root1.Next.Next.Next = comNodes
    root1.Print()

    root2 := &ListNode{Val: 4}
    root2.Next = &ListNode{Val: 5}
    root2.Next.Next = comNodes
    root2.Print()

    node := FindFirstCommonNode(root1, root2)
    fmt.Println(node.Val)
    node.Print()

    fmt.Println("++++++++++++++++++++++++++++++++++++++++++++++")
    head1 := &ListNode{Val: 1}
    head2 := &ListNode{Val: 2}
    head2.Next = &ListNode{Val: 3}
    node1 := FindFirstCommonNode(head1, head2)
    fmt.Println(node1)

}

// Insert 从链表节点尾部添加节点
func (ln *ListNode) Insert(val int) {
    if ln == nil {
        return
    }
    //创建一个新节点
    newNode := &ListNode{Val: val}
    cur := ln
    //找到链表的末尾节点
    for cur.Next != nil {
        cur = cur.Next
    }
    //末尾节点的next指针域连接新节点
    cur.Next = newNode
}

// Print 从链表头结点开始打印链表的值
func (ln *ListNode) Print() {
    if ln == nil {
        return
    }
    cur := ln
    //遍历每一个节点
    for cur != nil {
        fmt.Print(cur.Val, "\t")
        cur = cur.Next //更改指针变量的指向
    }
    fmt.Println()
}

如果上面的代码理解的不是很清楚,你可以参考视频的详细讲解。

4.小结

获取两链表的第一个公共节点可以采用如下步骤完成:(1)定义两个指针变量,分别指向两个链表的头节点。(2)移动两个指针变量。移动到末尾要注意指向的改变,即原来指向的链表1,现在要指向链表2;(3)返回公共节点。

更多数据结构与算法视频讲解,你可以从以下地址找到:

对于链表的相关操作,我们总结了一套【可视化+图解】方法,依据此方法来解决链表相关问题,链表操作变得易于理解,写出来的代码可读性高也不容易出错。具体也可以参考视频详细讲解。

今日佳句:纸上得来终觉浅,绝知此事要躬行。


好易学数据结构
1 声望0 粉丝