在解题没有思路的时候,有一个办法就是将常用数据结构和常用算法思想都想一遍,看看能解决哪些问题。
常用的数据结构有:
- 数组
- 链表
- 队
- 栈
- Hash
- 集合
- 树
- 堆
常用的算法思想有:
- 查找
- 排序
- 双指针
- 递归
- 迭代
- 分治
- 贪心
- 回溯
- 动态规划
这是一道经典的算法题,剑指Offer52:输入两个链表,找到它们的第一个公共子节点。
对于这种题,首先想到的就是蛮力法,将第一个链表的每一个节点依次与第二个链币的每一个节点进行比较,直到找到第一个相同的节点为止,但是这种方法的时间复杂度太高,排除!
再看队列和栈,队列在这道题中没有什么用,但是用栈是可以解决问题的。将两个链表同时压到栈里,之后一边同时出栈,一边比较出栈元素是否一致,如果一致就说明存在相交。之后继续出栈,直到找到最后一组出栈元素相等的那个就是初始公共节点。
接着来看Hash和集合,也是可以解决问题的。首先将第一个链表中的节点全部存到Map里,然后一边遍历第二个链表,一边检测Hash中是否存在当前节点。如果两个链表有交点,那就找到了。同样的,集合也是这样的。
这个时候就可以直接跟面试官说,应该可以用HashMap做,另外用集合和栈也能够解决问题,很明显面试官接下来就会问你,怎么解决?
然后你这时候就可以和面试官吹水说用HashMap,集合和栈具体怎么解决,面试官一听:哎呦,不错哦。
当然,如果你想错了,比如你开始的时候跟面试官说队列可以,后面发现队列根本解决不了,这个时候你也不要慌,直接跟面试官说“队列不行,我想想其它办法”就OK了,面试官一般不会深究。算法面试本身就是一个相互交流的过程,如果有些地方不清楚,他甚至会提醒你一下,所以不用紧张。
除了上面提到的栈、Hash和集合两种方法,还有两种巧妙的方法能够解决这个问题,一个是拼接两个字符串,一个是差和双指针,先在这里留个悬念,我们先来看如何用栈、Hash和集合解决。
哈希和集合
对于本题,使用集合代码更为简洁,先将第一个链表的节点遍历全部存到集合中,然后一边遍历第二个链表,一边判断是否在集合中存在。直接上代码:
/**
* 使用哈希和集合找到公共节点
* @param headA 第一个链表的头节点
* @param headB 第二个链表的头节点
* @return 公共节点或者null
*/
public static ListNode FindFirstCommonNodeBySet(ListNode headA, ListNode headB) {
// 先把第一个链表存到集合中
Set<ListNode> set = new HashSet<>();
while (headA != null) {
set.add(headA);
headA = headA.next;
}
// 遍历第二个链表
while (headB != null) {
// 判断第二个链表节点是否在集合中
if (set.contains(headB)) {
return headB;
}
headB