题目:找出两个链表第一个公共子节点
题目链接:剑指 Offer 52. 两个链表的第一个公共节点
解决方法
源码地址:GitHub-golang版本
注意:比较是否为公共节点,需要使用该节点的地址进行比对
方法1:使用HashMap或集合
说明:将链表1遍历后存入HashMap中,然后遍历链表2,依次判断是否在HashMap中存在,匹配的第一个就是第一个公共子节点。
// 返回找到的公共结点,没找到时返回list1
func Method(list1 *slink.Cat, list2 *slink.Cat) (bool, *slink.Cat){
if list1.Next == nil || list2.Next == nil {
return false, list1
}
list1_map := make(map[*slink.Cat]*slink.Cat)
// 这里单向链表,head头结点不存数据,直接跳过
list1_tmp := list1.Next
// 遍历list1,放到hashmap
for {
list1_map[list1_tmp] = list1_tmp
if list1_tmp.Next == nil {
break
}
list1_tmp = list1_tmp.Next
}
find := false
// 同理:单向链表,head头结点不存数据,直接跳过
list2_tmp := list2.Next
// 遍历list2,找到hashmap中存在的点 就是公共结点
for {
_, ok := list1_map[list2_tmp]
if ok {
find = true
break
}
if list2_tmp.Next == nil {
break
}
list2_tmp = list2_tmp.Next
}
if find {
return find, list2_tmp // 返回公共节点
}
return find, list1
}
方法2:使用栈
说明:将两个链表分别入两个栈中,然后同步从栈里取值,直到取出的值不相等的上一个值就是第一个公共子节点了。(注意:当两个链表没有公共节点时,从栈取值第一个就是不同的,这时候也要记得判断。)
// 返回找到的公共结点,没找到时返回list1
func Method(list1 *slink.Cat, list2 *slink.Cat) (bool, *slink.Cat){
if list1.Next == nil || list2.Next == nil {
return false, list1
}
find := false
var pre *slink.Cat
stack1 := List2Stack(list1)
stack2 := List2Stack(list2)
// 出栈比较
for {
tmp1, err1 := stack1.Pop()
if err1 != nil{
fmt.Println("err1: ", err1)
break
}
tmp2, err2 := stack2.Pop()
if err2 != nil {
fmt.Println("err2: ", err2)
break
}
ntmp1, _ := tmp1.(*slink.Cat)
ntmp2, _ := tmp2.(*slink.Cat)
if ntmp1 != ntmp2 {
// 结点地址相同
find = true
break
} else {
pre = ntmp1
}
}
if find && pre != nil {
return find, pre
}
return false, list1
}
方法3:拼接等长
说明:将链表1和链表2互相拼接上对方后,新的链表1和新的链表2长度一致,这样就可以同步遍历找到第一个相同结点,就是第一个公共子节点了。(优化:无需创建新的链表1和链表2,直接开始同步遍历两个链表,当任意一个到末尾后紧接着另一个链表遍历下去即可。)
// 返回找到的公共结点,没找到时返回list1
func Method(list1 *slink.Cat, list2 *slink.Cat) (bool, *slink.Cat){
if list1.Next == nil || list2.Next == nil {
return false, list1
}
list1_tmp := list1.Next
list2_tmp := list2.Next
find := false
list1_joined := false
list2_joined := false
for {
// 找到了
if list1_tmp == list2_tmp {
find = true
break
}
list1_tmp = list1_tmp.Next
list2_tmp = list2_tmp.Next
if list1_tmp == nil && list2_tmp == nil {
break
}
// 拼接对方
if list1_tmp == nil && !list1_joined{
list1_joined = true
list1_tmp = list2.Next
}
if list2_tmp == nil && !list2_joined{
list2_joined = true
list2_tmp = list1.Next
}
}
if find {
return find, list2_tmp
}
return find, list1
}
方法4:消除步差
说明:原理跟方法3差不多,求得链表1和链表2的长度差,让长的链表先走差的步数,接着两个链表的剩余长度就是一致的了,此时同步遍历找到第一个相同结点,就是第一个公共子节点了。
// 返回找到的公共结点,没找到时返回list1
func Method(list1 *slink.Cat, list2 *slink.Cat) (bool, *slink.Cat){
if list1.Next == nil || list2.Next == nil {
return false, list1
}
list1_tmp := list1.Next
list2_tmp := list2.Next
find := false
len1 := slink.GetListLen(list1)
len2 := slink.GetListLen(list2)
sub := 0
// 消除步差
if len1 >= len2 {
sub = len1 - len2
for ; sub>0; sub-- {
list1_tmp = list1_tmp.Next
}
} else {
sub = len2 - len1
for ; sub>0; sub-- {
list2_tmp = list2_tmp.Next
}
}
for {
// 找到了
if list1_tmp == list2_tmp {
find = true
break
}
if list1_tmp.Next == nil {
break
}
list1_tmp = list1_tmp.Next
list2_tmp = list2_tmp.Next
}
if find {
return find, list2_tmp
}
return find, list1
}