BFS算法(Broad First Search,广度优先搜索):
方法:从图的某一节点开始,首先依次访问该节点的所有邻接节点,再按照这些顶点被访问的次序依次访问与它们相邻的所以未被访问的节点,重复此过程,直至所有节点均被访问为止,利用队列是经常的,类似于树中的层次遍历,一层一层的遍历,经常用于求最短路径问题。
基本框架是:
int BFS(Node start, Node target){
Queue<Node> q;//核心数据结构
Set<Node> visited;//避免走回头路
q.offer(start);//将起点加入对列
visited.add(start);
int step = 0;//记录扩散的步数
while(q not empty){
int sz = q.size();
/*将当前队列中的所有节点向四周扩散*/
for(int i = 0; i < sz; ++i){
Node cur = q.poll();
/*划重点:这里判断是否到达终点*/
if(cur is target)
return step;
/*将cur 的相邻节点加入队列*/
for(Node x : cur.adj())
if(x not in visited){
q.offer(x);
visited(x);
}
}
/*划重点:在这里更新步数*/
step++;
}
}
DFS也可以找到最短距离:
DFS(Depth First Search,深度优先搜索):
方法:在访问图中某一起始节点w出发,访问它的任一邻接结点w1,再从这一邻接节点出发,访问与其邻接但还未被访问的节点w2,再从这一w2出发,进行类似的访问,如此进行下去,直到到达所有的邻接结点都被访问过的结点u为止,接着退后一步,推到刚访问过的结点,看是否还有其他未被访问过的邻接结点,如果有,则访问此顶点,之后再进行与前述类似的访问,若果没有,就再退后一步进行搜索。重复上述过程,直至连通中所有的节点都被访问过为止。类似于树的先根遍历。
但用DFS求最短距离低效,因为它要把所有的节点都访问到,然后比较各路径长度。
但是BFS的空间复杂度高,DFS的空间复杂度低,因为队列每一次都要将一层的节点存完。
因此一般来说,再找最短路径的时候使用BFS,其他时候还是用DFS多一点(主要是递归代码好写)。
双向BFS有时会进一步提升效率,但是空间复杂度相同。
双指针技巧套路框架:
双指针技巧分为两类:“快慢指针”,“左右指针”。
快慢指针一般会初始化指向链表的头结点head,前进时快指针fast在前,慢指针slow在后面,巧妙解决一些链表中的问题。
快慢指针的常用算法:
1.确定链表中是否有环:如果没环,那么fast最终会指向null,如果含有环,那么快指针最终会超慢指针一圈,和慢指针相遇。
2.已知链表中有环,返回这个环的起始位置:当快慢指针相遇时,让其中任意一个指向head,然后让两个节点一相同的速度前进,再次相遇时所在的节点位置就是环开始的位置,原因:第一次相遇时,假设慢指针slow走了k 步,那么快指针一定走了2k步(快指针的速度为慢指针的2倍),就是所比慢指针多走了k 步(环长度的整数倍)。设相遇点与环的起点的距离为m,那么环的起点与头结点的距离为k-m,也就是说从head前进k-m步,也恰好到达环起点。所以只要我们把快慢指针中的任意一个重新指向head,然后两个指针同速前进 k-m 步就会相遇,相遇之处就是环的起点。
3.寻找无环单链表的中点:让快指针每次前进两步,慢指针一次一步,当快指针到达链表尽头是,慢指针就处于链表的中间位置。当链表的长度是奇数时,slow正好停在中点位置,长度为偶数时,slow最终的位置是中间偏右。
寻找单链表的倒数第k 各元素:让快指针先走k步,然后快慢指针开始同速前进。这样,当快指针到底链表末尾null时,慢指针所在的位置就是倒数第k 的链表节点。
左右指针的常用算法:
1.二分搜索;
2.两数之和:例如,在一个已按升序排列的有序数组和一个目标值,在数组中找到两个数使得它们相加之和等于目标值,返回这两个数的索引;
3.反转数组;
4.滑动窗口算法。