[Go版]算法通关村第一关白银——链表经典问题之找出两个链表第一个公共子节点

题目:找出两个链表第一个公共子节点

题目链接:剑指 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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值