leetcode第25题

简介: 时间复杂度:while 循环中本质上我们只是将每个结点访问了一次,加上结点倒置访问的一次,所以总共加起来每个结点其实只访问了 2 次。所以时间复杂度是 O(n)。空间复杂度:O(1)。解法二递归有没有被解法一的各种指针绕晕呢,我们有一个更好的选择,递归,这样看起来就会简洁很多。public ListNode reverseKGroup(ListNode head, int k) { if (head == null) return null; ListNode point = head; //找到子链表的尾部 int i = k;

image.png

top25

将一个链表,每 k 个倒置,最后一组不足 k 个就不倒置。

解法一 迭代

关于单链表倒置,我们在第 2 题就讨论过。有了单链表倒置,这道题无非就是用一个循环,每次将 k 个结点取下来,倒置后再接回去,然后再取 k 个,以此循环,到了最后一组如果不足 k 个,不做处理,直接返回头结点就可以了。所以关键就是,指针指来指去,大家不晕掉就好,我做了图示,大家参考一下。

为了将头结点也一般化,我们创建一个 dummy 结点,然后整个过程主要运用三个指针, tail 指针表示已经倒置后的链表的尾部,subhead 指针表示要进行倒置的子链表,toNull 指针为了将子链表从原来链表中取下来。

image.png

一个 while 循环,让 toNull 指针走 k - 1 步使其指向子链表的尾部。中间的 if 语句就是判断当前节点数够不够 k 个了,不够的话直接返回结果就可以了。

image.png

将子链表指向 null ,脱离出来。并且用 temp 保存下一个结点的位置。

image.png

image.png

接下来四步分别是,新链表接到 tail(注意下边的图 tail 是更新后的位置,之前 tail 在 dummy 的位置) 的后边;更新 tail 到新链表的尾部,也就是之前的 subhead (下图 subhead 也是更新后的位置,之前的位置参见上边的图);sub_head 更新到 temp 的位置;toNull 到 sub_head 的位置;然后将新的尾部 tail 把之前断开的链表连起来,接到 sub_head 上。

image.png

image.png


publicListNodereverseKGroup(ListNodehead, intk) {
if (head==null)
returnnull;
ListNodesub_head=head;
ListNodedummy=newListNode(0);
dummy.next=head;
ListNodetail=dummy;
ListNodetoNull=head;
while (sub_head!=null) {
inti=k;
//找到子链表的尾部while (i-1>0) {
toNull=toNull.next;
if (toNull==null) {
returndummy.next;
            }
i--;
        }
ListNodetemp=toNull.next;
//将子链表断开toNull.next=null;
ListNodenew_sub_head=reverse(sub_head); 
//将倒置后的链表接到 tail 后边tail.next=new_sub_head;
//更新 tail tail=sub_head; //sub_head 由于倒置其实是新链表的尾部sub_head=temp;
toNull=sub_head;
//将后边断开的链表接回来tail.next=sub_head;
    }
returndummy.next;
}
publicListNodereverse(ListNodehead) {
ListNodecurrent_head=null;
while (head!=null) {
ListNodenext=head.next;
head.next=current_head;
current_head=head;
head=next;
    }
returncurrent_head;
}

时间复杂度:while 循环中本质上我们只是将每个结点访问了一次,加上结点倒置访问的一次,所以总共加起来每个结点其实只访问了 2 次。所以时间复杂度是 O(n)。

空间复杂度:O(1)。

解法二递归

有没有被解法一的各种指针绕晕呢,我们有一个更好的选择,递归,这样看起来就会简洁很多。

publicListNodereverseKGroup(ListNodehead, intk) {
if (head==null)
returnnull;
ListNodepoint=head;
//找到子链表的尾部inti=k;
while(i-1>0){
point=point.next;
if (point==null) {
returnhead;
        }
i--;
    }
ListNodetemp=point.next;
//将子链表断开point.next=null;
//倒置子链表,并接受新的头结点ListNodenew_head=reverse(head);
//head 其实是倒置链表的尾部,然后我们将后边的倒置结果接过来就可以了//temp 是链表断开后的头指针,可以参考解法一的图示head.next=reverseKGroup(temp,k);
returnnew_head;
}
publicListNodereverse(ListNodehead) {
ListNodecurrent_head=null;
while (head!=null) {
ListNodenext=head.next;
head.next=current_head;
current_head=head;
head=next;
    }
returncurrent_head;
}


复杂度:递归留坑。

还是那句话,涉及到链表的,我们就画下图,把各个指针的移动理清楚,一般没啥问题。

相关文章
|
5月前
LeetCode
LeetCode
37 0
|
5月前
|
Java
leetcode-474:一和零
leetcode-474:一和零
38 0
|
Python
LeetCode 1904. 你完成的完整对局数
一款新的在线电子游戏在近期发布,在该电子游戏中,以 刻钟 为周期规划若干时长为 15 分钟 的游戏对局。这意味着,在 HH:00、HH:15、HH:30 和 HH:45 ,将会开始一个新的对局,其中 HH 用一个从 00 到 23 的整数表示。游戏中使用 24 小时制的时钟 ,所以一天中最早的时间是 00:00 ,最晚的时间是 23:59 。
101 0
|
存储 Python
LeetCode 66. Plus One
给定表示非负整数的非空数字数组,加上整数的1。 存储数字使得最高有效数字位于列表的开头,并且数组中的每个元素包含单个数字。 您可以假设整数不包含任何前导零,除了数字0本身。
88 0
LeetCode 66. Plus One
|
存储 算法
leetcode第43题
个位乘个位,得出一个数,然后个位乘十位,全部乘完以后,就再用十位乘以各个位。然后百位乘以各个位,最后将每次得出的数相加。十位的结果要补 1 个 0 ,百位的结果要补两个 0 。相加的话我们可以直接用之前的大数相加。直接看代码吧。
leetcode第43题
|
算法
leetcode第45题
时间复杂度:O(n)。 空间复杂度:O(1)。 这里要注意一个细节,就是 for 循环中,i < nums.length - 1,少了末尾。因为开始的时候边界是第 0 个位置,steps 已经加 1 了。如下图,如果最后一步刚好跳到了末尾,此时 steps 其实不用加 1 了。如果是 i < nums.length,i 遍历到最后的时候,会进入 if 语句中,steps 会多加 1 。
101 0
leetcode第45题
leetcode第54题
在 leetcode 的 solution 和 discuss 看了下,基本就是这个思路了,只是实现上有些不同,怎么用来标记是否走过,当前方向,怎么遍历,实现有些不同,但本质上是一样的。就是充分理解题意,然后模仿遍历的过程。
100 0
leetcode第54题
|
人工智能 算法
leetcode第41题
对于这种要求空间复杂度的,我们可以先考虑如果有一个等大的空间,我们可以怎么做。然后再考虑如果直接用原数组怎么做,主要是要保证数组的信息不要丢失。目前遇到的,主要有两种方法就是交换和取相反数。
leetcode第41题
leetcode第46题
这是自己开始想到的一个方法,考虑的思路是,先考虑小问题怎么解决,然后再利用小问题去解决大问题。没错,就是递归的思路。比如说, 如果只有 1 个数字 [ 1 ],那么很简单,直接返回 [ [ 1 ] ] 就 OK 了。 如果加了 1 个数字 2, [ 1 2 ] 该怎么办呢?我们只需要在上边的情况里,在 1 的空隙,也就是左边右边插入 2 就够了。变成 [ [ 2 1 ], [ 1 2 ] ]。
leetcode第46题