您的位置:首页 > Web前端

剑指Offer:两个单链表的第一个公共结点

2018-02-24 21:44 232 查看
输入两个单链表,找出这两个链表的第一个公共结点。

实验代码:https://pan.baidu.com/s/1jKemT6I

解法一:暴力查找

遍历链表1,获得结点node1,再次遍历链表2,获得结点node2,比较node1和node2,如果相等说明找到结点,否则查找node2的下一个结点,继续比对,如果遍历完这个链表2也找不到说明node1不是公共结点,继续遍历链表1获得node1的下一个结点,再次遍历链表2进行相同的比较操作。

假设链表1的长度为m,链表2的长度为n,那么暴力查找的时间复杂度为O(mn)。

实现代码:

private static LinkNode FindFirstCommonNode(SingleLinkList list1,SingleLinkList list2){
if(list1==null || list2==null || list1.getHead()==null || list2.getHead()==null){
return null;
}
//遍历list1
LinkNode node1 = list1.getHead();
while(node1!=null){
//遍历list2
LinkNode node2 = list2.getHead();
while(node2!=null){
if(node1==node2){
return node1;
}
node2 = node2.getNext();
}
node1 = node1.getNext();
}
return null;
}


解法二:使用辅助空间,时间复杂度为O(m+n)

我们现在来分析具备公共结点的单链表有什么特性。如果两个单链表具备公共结点,那么在第一个公共结点开始每个结点的next域都是相同的,所以从第一个公共结点后两个链表的元素都是相同的,不会出现分叉。即拓扑形状为“Y”形,不可能是“X”形。



我们可以从链表尾部从后往前比对,最后一个相等的结点就是第一个公共结点。但是,单链表无法向前遍历,从前往后遍历时尾部结点后面才出现,而我们想先比较尾部,这就要“先进后出”,我们很容易相到栈结构。创建两个栈分别存放链表的每一个结点,这样尾部结点就在栈顶,我们比较栈顶元素是否相同,如果相同就弹出,继续比较栈顶,直至最后一个相等的结点,也就是我们需要的结果了。

实现代码:

private static LinkNode FindFirstCommonNode(SingleLinkList list1,SingleLinkList list2){
if(list1==null || list2==null || list1.getHead()==null || list2.getHead()==null){
return null;
}
Stack stack1 = new Stack(list1.length());
Stack stack2 = new Stack(list2.length());

//压入list1的元素
LinkNode node1 = list1.getHead();
while(node1!=null){
stack1.Push(node1);
node1 = node1.getNext();
}

//压入list2的元素
LinkNode node2 = list2.getHead();
while(node2!=null){
stack2.Push(node2);
node2 = node2.getNext();
}

LinkNode comNode = null;
while(!stack1.isEmpty()&&!stack2.isEmpty()){
if(stack1.Peek()==stack2.Peek()){//比较栈顶是否相同,但是不弹出栈顶
comNode = stack1.Pop();//尾部结点相同,保存到comNode中,并且弹出栈顶
stack2.Pop();
}else{
break;
}
}
return comNode;
}


通过是否辅助栈我们将时间复杂度为O(mn)的解法降到了O(m+n),这是空间换时间的解法。

解法三:不使用辅助空间,时间复杂度为O(m+n)

上面的解法中我们运用了拥有共同结点的单链表第一个公共结点后结点相同,且从后比较最后一个相同的结点就是第一个公共结点的这一特点实现了O(m+n)的解法,但是这需要我们多消耗一部分空间(虽然现在的PC不在乎),我们现在依旧利用类似的特点,只是这次我们从头开始遍历两个链表,分别比较两个链表上的结点是否相同,第一个相同的就是我们想要的结果。

但是,我们不可以同时遍历两个链表,因为第一个公共结点前的长度不同。比如上图所示的例子,list1比list2多1个结点,所有我们要让在list1先走一步再和list2的结点相比较。

实现代码:

private static LinkNode FindFirstCommonNode(SingleLinkList list1,SingleLinkList list2){
if(list1==null || list2==null || list1.getHead()==null || list2.getHead()==null){
return null;
}
LinkNode comNode = null;
LinkNode node1 = list1.getHead();
LinkNode node2 = list2.getHead();

int k = Math.abs(list1.length()-list2.length());

if(list1.length()<list2.length()){
node1 = list2.getHead();
node2 = list1.getHead();
}
//先在长的链表上走k步
for(int i=0;i<k;i++){
node1 = node1.getNext();
}
//开始比较
while(node1!=node2){
node1 = node1.getNext();
node2 = node2.getNext();
}
comNode = node1;
return comNode;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息