到现在面试经历过很多次了,却很少有成功,之前拒掉了几次面试,现在非常后悔。
这里记录下之前面试的他们的算法题
1. 如何判断一个链表有环
- 快慢指针可以解决这个问题
使用快慢指针。这里为什么会相遇?最坏的打算当慢指针走一圈的时候,快指针可以走两圈,所以刚好会在同一个点上面。
如果最后指向null,则说明没有环,如果最后走到了相同点,则说明有环。
- 如何判断该环的起点在什么地方

- 可以假设 环起点 到相遇点的距离为 m
- 那么head到 环起点 刚好是 k-m
- 因为慢指针走了 k 步快指针走了 2k 步相遇了,那么相遇点到快指针的相遇点(N圈之后的相遇点)距离就变成了 2k-k=k 距离一样。那么剪掉相同的环起点到相遇点的 m 都变成了 k-m 步。这样,把任何一个指针的头指针指向 head,用相同的速度,再次相遇点,就是环起点。
2. 两条链表是否相交
要判断两个链表是否相交,可以判断有没有共同部分,那么共同部分怎么判断呢?
- 最原始的办法:使用map(映射)来记录每个node的信息,java中地址值不存在,可以直接判断引用是否一致。golang可以根据地址是否一致,也可以记录value是否一致(当然存在相同的value的情况就不行了)
- 使用特殊手法,如下图

- 双指针同时进行
- 迭代A链表,结束后,迭代B链表
- 迭代B链表,结束后,迭代A链表
- 在迭代的时候,判断两个指针是否一致,如果存在一致
具体代码可以是
p1, p2 := headA, headB
for p1 != p2 {
if p1 == nil {
p1 = headB
} else {
p1 = p1.Next
}
if p2 == nil {
p2 = headA
} else {
p2 = p2.Next
}
}
return p1
这里有个问题,如果一直没有重复的,会怎么样呢?
这个问题比较好回答,因为 A+B 和 B+A 之后,那么他们的总长度是相同的,相同的总长度,如果没有重合的话,肯定是nil,那么肯定满足 p1 = p2 的条件。
3. 合并多个链表
合并两个链表可以在程序中,对比两个链表的val来进行合并,但是合并多个链表,就存在不一样的问题了,所以需要其他的处理方式。
所以这里需要用到了priority队列,所以首先要完成优先队列
type Queue []*ListNode
func (q Queue) Len() int{
return len(q)
}
func (q Queue)Less(i,j int) bool {
return q[i].Val < q[j].Val
}
func (q Queue)Swap(i, j int){
q[i],q[j] = q[j], q[i]
}
func (q *Queue) Push(v interface{}){
*q = append(*q, v.(*LinkNode))
}
func (q *Queue) Pop() interface{} {
old:= *q
n:=len(*q)
v:=*q[n-1]
*q = old[:n-1]
return v
}
这里为什么对Queue的方法,即有Queue对象的,又有Queue指针的呢?
Queue本质上是个 *ListNode 数组,其Len/Less/Swap是比较常见的数组用来sort需要定义的函数,而Push、Pop则是使用数组来插入、获取的方法
其中 1 实现的 sort/sort.go中的
type Interface interface接口,所以需要使用的是Queue对象。而 2 实现的是对Queue的插入、获取方法,这些方法如果需要对指针对象进行操作,如果不是指针对象,而是复制对象,那不会影响原来的值,就没有意义。
注:具体关于优先级别队列的,可以参照 网上介绍