前几天有个朋友去面试字节跳动,面试官问了他一道链表相关的算法题,不过他一时之间没作出来,就来问了我一下,感受这道题还不错,拿来说一讲。面试
这实际上是一道变形的链表反转题,大体描述以下算法
给定一个单链表的头节点 head,实现一个调整单链表的函数,使得每K个节点之间为一组进行逆序,而且从链表的尾部开始组起,头部剩余节点数量不够一组的不须要逆序。(不能使用队列或者栈做为辅助)函数
例如:spa
链表:1->2->3->4->5->6->7->8->null, K = 3。那么 6->7->8,3->4->5,1->2各位一组。调整后:1->2->5->4->3->8->7->6->null。其中 1,2不调整,由于不够一组。code
# 解答blog
这道题的难点在于,是从链表的尾部开始组起的,而不是从链表的头部,若是是头部的话,那咱们仍是比较容易作的,由于你能够遍历链表,每遍历 k 个就拆分为一组来逆序。可是从尾部的话就不同了,由于是单链表,不能日后遍历组起。不过这道题确定是用递归比较好作,对递归不大懂的建议看我以前写的一篇文章为何你学不会递归?告别递归,谈谈个人一些经验,这篇文章写了关于递归的一些套路。递归
先作一道相似的反转题队列
在作这道题以前,咱们不仿先来看看若是从头部开始组起的话,应该怎么作呢?例如:链表:1->2->3->4->5->6->7->8->null, K = 3。调整后:3->2->1->6->5->4->7->8->null。其中 7,8不调整,由于不够一组。leetcode
对于这道题,若是你不知道怎么逆序一个单链表,那么能够看一下我以前写的如何优雅着反转单链表rem
这道题咱们能够用递归来实现,假设方法reverseKNode()的功能是将单链表的每K个节点之间逆序(从头部开始组起的哦);reverse()方法的功能是将一个单链表逆序。
那么对于下面的这个单链表,其中 K = 3。
咱们把前K个节点与后面的节点分割出来:
temp指向的剩余的链表,能够说是原问题的一个子问题。咱们能够调用reverseKNode()方法将temp指向的链表每K个节点之间进行逆序。再调用reverse()方法把head指向的那3个节点进行逆序,结果以下:
接着,咱们只须要把这两部分给链接起来就能够了。最后的结果以下:
代码以下:
//k个为一组逆序
public ListNode reverseKGroup(ListNode head, int k) {
ListNode temp = head;
for (int i = 1; i < k && temp != null; i++) {
temp = temp.next;
}
//判断节点的数量是否可以凑成一组
if(temp == null)
return head;
ListNode t2 = temp.next;
temp.next = null;
//把当前的组进行逆序
ListNode newHead = reverseList(head);
//把以后的节点进行分组逆序
ListNode newTemp = reverseKGroup(t2, k);
// 把两部分链接起来
head.next = newTemp;
return newHead;
}
//逆序单链表
private static ListNode reverseList(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode result = reverseList(head.next);
head.next.next = head;
head.next = null;
return result;
}
回到本题
这两道题能够说是及其类似的了,只是一道从头部开始组起,这道从头部开始组起的,也是 leetcode 的第 25 题。而面试的时候,常常会进行变形,例如这道字节跳动的题,它变成从尾部开始组起,可能你一时之间就不知道该怎么弄了。固然,可能有人一会儿就反应出来,把他秒杀了。
其实这道题很好作滴,你只须要先把单链表进行一次逆序,逆序以后就能转化为从头部开始组起了,而后按照我上面的解法,处理完以后,把结果再次逆序即搞定。两次逆序至关于没逆序。
例如对于链表(其中 K = 3)
咱们把它从尾部开始组起,每 K 个节点为一组进行逆序。步骤以下
一、先进行逆序
逆序以后就能够把问题转化为从头部开始组起,每 K 个节点为一组进行逆序。
二、处理后的结果以下
三、接着在把结果逆序一次,结果以下
代码以下
public ListNode solve(ListNode head, int k) {
// 调用逆序函数
head = reverse(head);
// 调用每 k 个为一组的逆序函数(从头部开始组起)
head = reverseKGroup(head, k);
// 在逆序一次
head = reverse(head);
return head;
}
相似于这种须要先进行逆序的还要两个链表相加,这道题字节跳动的笔试题也有出过,以下图的第二题
这道题就须要先把两个链表逆序,再节点间相加,最后在合并了。
# 总结
关于链表的算法题,在面试的时候据说是挺常考的,你们能够多注意注意,遇到不错的链表算法题,也欢迎扔给我勒。