1.题目
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
进阶:
-
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
-
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
输入:head = [1], k = 1
输出:[1]
2.想法
这道题开始的时候感觉是有点困难,但是根据前一道题和同学商量的思想,有了想法,可以把这些看成一个List,List中有reverse方法。
- 把这个链表中的所有元素存入一个List中
- 根据划分的k,将list重新分割,装入新的list1,在分割过程中,分割后就使用reverse。
- 这些分割的小list1再装入一个list2中
- 新建一个节点,遍历list2 遍历list1
- 把其中的值都放入节点的next中,一直next下去,就完成翻转了。
2.1代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
//1.使用一个list 把这些数值都存入list中
if(k==1) return head;
if(head==null) return null;
ListNode pre = new ListNode();
pre.next = head;
List<List<Integer>> lists=new ArrayList<List<Integer>>();
List<Integer> li = new ArrayList<Integer>();
while(head!=null)
{
li.add(head.val);
head = head.next;
}
//2.根据k 把 一个list 存入k个元素.
//i和 k的关系 这一块 要写好嗄
for(int i = 0;i<li.size();i++)
{
List<Integer> li1 = new ArrayList<>();
if((i+1)%k==0)
{
for(int j = (i+1)-k;j<i+1;j++)
{
li1.add(li.get(j));
}
Collections.reverse(li1);
lists.add(li1);
}
}
//最后几个元素
List<Integer> li2 = new ArrayList<>();
for(int i = (li.size()/k)*k;i<li.size();i++)
{
li2.add(li.get(i));
}
//Collections.reverse(li2);
lists.add(li2);
ListNode n = new ListNode();
ListNode nn = n;
for(List<Integer> lis:lists)
{
for(Integer in:lis)
{
nn.next = new ListNode(in);
nn=nn.next;
}
}
return n.next;
}
}
这样虽然运行出来了,但是运行的时间复杂度和空间复杂度都比较高。接下来,看一下其他人的回答。
2.2其他想法
看到想法很多,找一个看得懂的来说,就是先计算链表的长度,再将链表化成k等份,在每个k等份中循环进行两两交换。
这个代码自己没写,这是评论中的代码:
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0), prev = dummy, curr = head, next;
dummy.next = head;
int length = 0;
while(head != null) {
length++;
head = head.next;
}
head = dummy.next;
for(int i = 0; i < length / k; i++) {
for(int j = 0; j < k - 1; j++) {
next = curr.next;
curr.next = next.next;
next.next = prev.next;
prev.next = next;
}
prev = curr;
curr = prev.next;
}
return dummy.next;
}
}
这个代码需要画图看得出来,我自己也有点小疑问:
就是在内层for循环中,他的curr没有向下next,怎么可以一直循环呢,我只画图使用了k=2时候的情况,但是k=3就要循环2次,我不知道这个是怎么循环的,当跳出循环的时候,之后的我没有问题。
3.总结
这道题自己写的时候使用了笨方法,知道怎么做就可以了,但是时间复杂度和空间复杂度都是相当高的了,就看了别人方法,这种类似的题都需要画图辅助才比较好,要仔细看这些链表的点指向哪里,我都没有静下心来仔细看,后还有就是我遇到那种有时链表赋值,忽然就搞不清了,有next倒还行,比如:
head= pre.next;
和
pre.next = head;
这两个我能分清一个,但是另一个就有时候转不过弯来。搜了下,找了个(有关head->next = p;和p=head->next;之间的区别)
说下自己的理解:
head.next = pre :就是pre是head的后驱 head是pre的前驱,图的第二种情况
head=pre.next:head的后驱和pre的后驱指向同一个点。
可以这么说,还得多看这样的题才好,还有知识点Collections.reverse(),这个coeelctions是有s的。
-----------------------------------
20210722更新
看知乎上说这是道面试题,就重新翻看了下,果然又有些忘了,哎,所以重新看了下题解。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy;
ListNode end = dummy;
while(end.next!=null)
{
for(int i=0;i<k&&end!=null;i++) end =end.next;
if(end == null) break;//最后几个就不翻转了
ListNode start = pre.next;
ListNode next = end.next;
end.next =null;
pre.next = reverse(start);
start.next = next;
pre =start;
end = pre;
}
return dummy.next;
}
public 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;
}
}
先得明白逻辑才行 ,一个pre,start,end,其中需要一个reverse方法,这些需要自己画图去看懂这个意思。先弄明白意思,再去编程。