1.基础问题:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
2.算法
这个基础问题比较简单,具体解法利用分治法的思想,将相邻两个节点看成一组,组内进行交换,组间再串联起来就可以解决这个问题了。
public class Solution24 {
public ListNode swapPairs(ListNode head) {
if (head==null||head.next==null)
return head;
ListNode pre=head,aft=head.next;
//两节点互换
pre.next=aft.next;
aft.next=pre;
//这一组交换完,下一组开始互换,并间组之间链接到一起
pre.next=swapPairs(pre.next);
return aft;
}
}
我这里是利用了递归,先算第一组,然后将剩下的链表节点当成新的链表进行递归翻转。算法的复杂度是O(N),因为链表有多少个节点就进行了多少次翻转操作。空间复杂度是O(N),因为整个链表就在递归过程中占用了链表大小的堆栈空间。
3.升级问题
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
4.算法
我对这题的思考是建立在前一个基础问题上的,刚开始也是根据分治法思想,先找出一组相对应的最后的翻转节点,然后组内进行翻转,再将组与组之间链接起来。但是在编写过程中忽略链表的一个特征就是链表重新链接需要先断开节点再链接,因为这步操作没有处理,使得我在处理K=3时少了一个节点,debug了半个多小时,最后看了leetcode的题解,对比自己的代码,才发现了这个致命错误。具体代码如下:
public class Solution25 {
public ListNode reverseKGroup(ListNode head, int k) {
if(head==null || k==1 || head.next==null)
return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy;
ListNode end = dummy;
int i = 0;
for (; i < k && end != null; i++) {//找到翻转节点
end = end.next;
}
//如果end==null或者i<k,说明k已经大于这一组的长度了,可以直接返回
if(i<k||end==null)
return head;
ListNode start = pre.next;
ListNode next = end.next;
end.next = null; //链表断开
pre.next = reverse(start);
start.next = reverseKGroup(next,k);//重新链接
return dummy.next;
}
//翻转函数
private ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
}
借鉴了题解,顺便把自己的代码改的比较容易理解和简洁。这样的话,这个算法的时间复杂度为 O(n*K), 最好的情况为 O(n) 最差的情况为O(n²),空间复杂度为 O(n),这点是没有满足使用常数级空间的要求,但是将上述代码改成非递归即可满足。非递归的如下链接 leetcode 25题 题解链接.
这个图用于帮助理解: