[剑指offer学习心得]之:两个链表的第一个公共结点
2016-10-05 19:18
441 查看
题目:输入两个链表,找出它们的第一个公共结点。
看到这道题,第一个想到的自然是暴力法:第一链表上顺序遍历每个结点,每遍历到一个结点,在第二个链表上顺序遍历每个结点。如果在第二个链表上有一个结点和第一个链表上结点相同,说明两个链表在这个结点上重合,于是找到了它们的公共结点。如果一个长度为m,一个长度为n,时间复杂度就是O(mn)
厉害了word哥,找个公共结点花了这么长时间,有没有其他办法?
其实还是有的,我们分析一下有公共结点的两个链表的特点就知道了。这两个链表从某一个结点开始,它们的next都指向同一个结点,所以从第一个公共结点开始,之后它们所有的结点都是重合的不可能出现分叉。
所以我们可以发现,如果两个链表有公共结点,那么公告结点出现在两个链表尾部,如果从后面往前比较,最后一个相同的不就是我们要找的第一个公告结点了嘛?从后往前,可以用到栈的方法,分别把两个链表结点放入两个栈里,这样两个链表的尾结点位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同就可以了。
但是上面思路需要两个辅助的栈,是要耗费空间的。空间复杂度为O(m+n)。所以还有第三个思路:
首先遍历两个链表得到它们的长度,就知道哪个链表比较长,以及长的链表比短的链表多几个结点。第二次遍历时,较长的链表先走若干步,接着同时在两个链表上遍历,找到的第一个相同的结点就是它们的第一个公共结点。
第三种思路和第二种思路相比,时间复杂度相同,但是不需要辅助的栈,提高了空间效率,当然是更好的。
所以可以考虑代码了。
注意,在此之前,当然是要准备好测试用例:
功能测试(两个链表有公共结点:第一个公共结点在链表的中间、末尾、头部;两个链表没有公共结点)
特殊输入测试(输入的链表头结点为null)
结果:
6
null
7
1
看到这道题,第一个想到的自然是暴力法:第一链表上顺序遍历每个结点,每遍历到一个结点,在第二个链表上顺序遍历每个结点。如果在第二个链表上有一个结点和第一个链表上结点相同,说明两个链表在这个结点上重合,于是找到了它们的公共结点。如果一个长度为m,一个长度为n,时间复杂度就是O(mn)
厉害了word哥,找个公共结点花了这么长时间,有没有其他办法?
其实还是有的,我们分析一下有公共结点的两个链表的特点就知道了。这两个链表从某一个结点开始,它们的next都指向同一个结点,所以从第一个公共结点开始,之后它们所有的结点都是重合的不可能出现分叉。
所以我们可以发现,如果两个链表有公共结点,那么公告结点出现在两个链表尾部,如果从后面往前比较,最后一个相同的不就是我们要找的第一个公告结点了嘛?从后往前,可以用到栈的方法,分别把两个链表结点放入两个栈里,这样两个链表的尾结点位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同就可以了。
但是上面思路需要两个辅助的栈,是要耗费空间的。空间复杂度为O(m+n)。所以还有第三个思路:
首先遍历两个链表得到它们的长度,就知道哪个链表比较长,以及长的链表比短的链表多几个结点。第二次遍历时,较长的链表先走若干步,接着同时在两个链表上遍历,找到的第一个相同的结点就是它们的第一个公共结点。
第三种思路和第二种思路相比,时间复杂度相同,但是不需要辅助的栈,提高了空间效率,当然是更好的。
所以可以考虑代码了。
注意,在此之前,当然是要准备好测试用例:
功能测试(两个链表有公共结点:第一个公共结点在链表的中间、末尾、头部;两个链表没有公共结点)
特殊输入测试(输入的链表头结点为null)
代码实现:
public class FindFirstCommonNode { public static void main(String[] args) { test1(); test2(); test3(); test4(); } private static void test1() { // 第一个公共结点在链表中间 // 1 - 2 - 3 \ // 6 - 7 // 4 - 5 / ListNode n1 = new ListNode(1); ListNode n2 = new ListNode(2); ListNode n3 = new ListNode(3); ListNode n4 = new ListNode(4); ListNode n5 = new ListNode(5); ListNode n6 = new ListNode(6); ListNode n7 = new ListNode(7); n1.next = n2; n2.next = n3; n3.next = n6; n6.next = n7; n4.next = n5; n5.next = n6; System.out.println(findFirstCommonNode(n1, n4)); // 6 } private static void test2() { // 没有公共结点 // 1 - 2 - 3 - 4 // // 5 - 6 - 7 ListNode n1 = new ListNode(1); ListNode n2 = new ListNode(2); ListNode n3 = new ListNode(3); ListNode n4 = new ListNode(4); ListNode n5 = new ListNode(5); ListNode n6 = new ListNode(6); ListNode n7 = new ListNode(7); n1.next = n2; n2.next = n3; n3.next = n4; n5.next = n6; n6.next = n7; System.out.println(findFirstCommonNode(n1, n5)); // null } private static void test3() { // 公共结点是最后一个结点 // 1 - 2 - 3 - 4 \ // 7 // 5 - 6 / ListNode n1 = new ListNode(1); ListNode n2 = new ListNode(2); ListNode n3 = new ListNode(3); ListNode n4 = new ListNode(4); ListNode n5 = new ListNode(5); ListNode n6 = new ListNode(6); ListNode n7 = new ListNode(7); n1.next = n2; n2.next = n3; n3.next = n4; n4.next = n7; n5.next = n6; n6.next = n7; System.out.println(findFirstCommonNode(n1, n5)); // 7 } private static void test4() { // 公共结点是第一个结点 // 1 - 2 - 3 - 4 - 5 // 两个链表完全重合 ListNode n1 = new ListNode(1); ListNode n2 = new ListNode(2); ListNode n3 = new ListNode(3); ListNode n4 = new ListNode(4); ListNode n5 = new ListNode(5); ListNode n6 = new ListNode(6); ListNode n7 = new ListNode(7); n1.next = n2; n2.next = n3; n3.next = n4; n4.next = n5; System.out.println(findFirstCommonNode(n1, n1)); // 1 } /** * 链表结点类 */ private static class ListNode { int val; ListNode next; public ListNode() { } public ListNode(int val) { this.val = val; } @Override public String toString() { return val + ""; } } /** * * @param head1 第一个链表 * @param head2 第二个链表 * @return 找到的公共结点,没有返回null */ private static ListNode findFirstCommonNode(ListNode head1,ListNode head2){ //首先得到两个链表的长度 int length1=getListLength(head1); int length2=getListLength(head2); int dif=length1-length2; ListNode longListHead=head1; ListNode shortListHead=head2; if(dif<0){ longListHead=head2; shortListHead=head1; dif=length2-length1; } //长链表先走几步 for(int i=0;i<dif;i++){ longListHead=longListHead.next; } while((longListHead!=null)&&(shortListHead!=null)&&(longListHead!=shortListHead)){ longListHead=longListHead.next; shortListHead=shortListHead.next; } //得到第一个公共结点并返回 return longListHead; } /** * 获取链表的长度 * @param head * @return */ private static int getListLength(ListNode head){ int result=0; while(head!=null){ result++; head=head.next; } return result; } }
结果:
6
null
7
1
相关文章推荐
- 《剑指Offer》学习笔记--面试题37:两个链表的第一个公共结点
- 剑指offer系列源码-两个链表的第一个公共结点
- 剑指offer(33)-两个链表的第一个公共结点
- 剑指offer——两个链表的第一个公共结点
- 剑指offer:(37)时间效率和空间效率的平衡 :两个链表的第一个公共结点
- 剑指Offer之面试题37:两个链表的第一个公共结点
- 剑指offer——面试题37:两个链表的第一个公共结点(哈希,栈)
- 《剑指offer》-两个链表的第一个公共结点
- 剑指Offer 37 两个链表的第一个公共结点
- 剑指Offer - 九度1505 - 两个链表的第一个公共结点
- 剑指offer——两个链表的第一个公共结点
- 《剑指offer》——两个链表的第一个公共结点
- 剑指offer—两个链表的第一个公共结点
- 剑指Offer: 两个链表的第一个公共结点
- 两个链表的第一个公共结点(剑指offer)+链表
- 《剑指offer》两个链表的第一个公共结点
- 剑指offer:两个链表的第一个公共结点输入两个链表,找出它们的第一个公共结点。
- 【剑指Offer学习】【面试题37:两个链表的第一个公共结点】
- 剑指offer之面试题37:两个链表的第一个公共结点[LeetCode 160] 解题报告
- 牛客:剑指offer:两个链表的第一个公共结点(Java)