题目:输入两个链表,找到第一个公共子节点,例如下面两个链表:
两个链表的头结点是已知的,相交之后形成一个单链表,但相交位置是未知的 ,相交之前节点数也是未知的,求相交点。
没有思路怎么解题:将常用的数据结构和算法思想都想一遍,看看那个能解决问题,
回顾一下
常用的数据结构有数组、链表、队、栈、Hash、集合、树、堆。
常用的算法思想有查找、排序、双指针、递归、迭代、分治、贪心、回溯和动态规划等待。
冒泡排序,实现简单但复杂度高,排除!
Hash,将第一个链表存到Hash中,然后一边遍历第二个链表,一边检测当前元素是否存在Hash中,如果有交点,就会找到,集合同理。
栈,将两个链表分别压入栈中,因为存在交点,后部分为相同的单链表(图的相交点之后),所以后压入的结点会有一段相等的加点,然后依次弹出,比较两个结点是否一致,队后出栈的一组一致结点就是要找的位置。
面试中可以将所有思路跟面试官讲,看面试官想要那种,即使错了,也可以重新思考,甚至还会提醒,这个我们要记得。
一个链表存入栈中遍历另一个链表进行比较,有交点则一定能检测出来,使用集合代码会简便,看代码:注意代码注释
首先规范LIstNode类
public class ListNode {
private int val;
ListNode next;
ListNode(int x){
val = x;
next = null;
}
}
public ListNode find(ListNode headA ,ListNode headB){
HashSet set = new HashSet<>();
//将A链表存入集合中
while (headA != null){
set.add(headA);
headA = headA.next;
}
//遍历B和集合中元素比较
while(headB != null){
if (set.contains(headB)){
return headB;
}
headB = headB.next;
}
return null;
}
将两个链表分别入栈,然后分别出栈,如果相等就继续出栈,一直找到最晚出栈的一组就是要找的交点,需要两个O(n)的空间。
代码:
public ListNode stack(ListNode headA,ListNode headB){
//1.创建两个空栈,用来存储元素
Stack stackA = new Stack();
Stack stackB = new Stack();
//2.分别压入栈中
while(headA != null){
stackA.push(headA);
headA = headA.next;
}
while(headB != null){
stackB.push(headA);
headB = headB.next;
}
//3.分别出栈然后比较,
//最后一个弹出的结点
ListNode preNode = null;
//判断条件,栈空
while(stackB.size() >0 && stackA.size()>0){
//peek()查看栈顶,pop()弹出并打印
if (stackA.peek() == stackB.peek()){
preNode = stackA.pop();
stackB.pop();
}else {
//此时交点之后结点全部弹出
break;
}
}
return preNode;
}
A : 0-1-2-3-4-5
B : a-b-4-5
拼接之后:
AB:0-1-2-3-4-5-a-b-4-5
BA : a-b-4-5-0-1-2-3-4-5
发现从4之后开始两个结点一样了,4就是要找的结点,以交点为中心,将链表分别分为left_a和right_a,left_b和right_b,此处有点难以理解,看图:
所以找到最后的4结点就好,两个链表轮流遍历,代码:
//pHead1和pHead2为AB和BA的头
public ListNode find(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;//排除空链表
}
ListNode p1 = pHead1;
ListNode p2 = pHead2;
//两者同时遍历
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
//如果这里不判断,没有公共节点的时候,一条链表遍历完会直接切换另一条链表 ,直接切换没有终止条件,(例如都遍历到最后又会重新遍历)
if (p1 != p2) {
//一个链表访问完了就跳到另一个链表访问,此时说的是A或者B
if (p1 == null) {
p1 = pHead2;
}
if (p2 == null) {
p2 = pHead1;
}
}
}
return p1;
}
第一轮遍历,假设La长度L1,Lb长度为L2,则|L2 - L1| 就是两个结点的差值,第二轮遍历,长的先走|L2 - L1|,然后两个结点同时向前走,结点一样时候就是公共结点,(简单理解,同时遍历的时候,在交点之前,长的会多走一点,以致于错过一点,短的会先经过交点,计算出来多走的,第二轮长的先走一点,两者就会同时到达公共点)。
代码实现:
public ListNode find(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode current1 = pHead1;
ListNode current2 = pHead2;
int l1 = 0, l2 = 0;
//分别统计两个链表长度
while (current1 != null){
current1 = current1.next;
l1++;
}while (current2 != null){
current2 = current2.next;
l2++;
}
current1 = pHead1;
current2 = pHead2;
int sub = l1-l2>0?l1-l2:l2-l1;
//长的先走sub步
if(l1 >l2){
int a = 0;
while (a
你可能感兴趣的:(算法通关村专栏,算法,链表,数据结构)