作为一个菜鸟,最近在看《我的第一本算法书》,想在之前薄弱的基础上,多了解一点。虽然目前的工作重心不在算法上,但是多会一点总不是什么坏事
《我的第一本算法书》很通俗易懂,配了大量直白的图片,让我能够以很快的速度去了解之前没接触过的知识点, 但问题也在于过于通俗易懂了,跟看连环画一样,容易遗忘。因此在翻了一部分之后,回到前面,根据每一节的知识点,到LeetCode上找相应的题目来做,以加深理解。
根据标签“链表”找题目的时候,看到两题“困难”还是有点怵的,没有看评论和题解,且在做的时候经常遗漏一些关键点,整段代码删删改改了好多次,虽然最后做出来了,但是在用时和内存消耗上不是很好。
以下的解题思路是最后做完情况下的思路归纳,有些错误过程已经遗忘了。
可能是我表述能力不行,没办法很简洁的表达出来。
题目描述:
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group
解题思路:
- 根据说明,知道需要更改的是每一个节点的next值,每个节点中的val值不能做任何改变;
- 图一为一开始时的链表。
- 当k值为1时,直接返回头结点即可。
- 当k>2时,需要更改为图二的状态,相对来说比较简答。
a、找到1、3,next值互换;
b、找3的同时,保存2这个节点,换1、3的next值时,更换2的next值。此处需要说明的是
强调:1、2、3、4、5、6、7数据时,k=5,那么1、5在互换时,也需要保存4这个节点地址,因为4的指向变化了,不 再指 向5,而是指向1,换完可得5、2、3、4、1;同理,互换2、4时,需要保存节点3地址,因为3需要指向2。
c、找3的同时,保存1、4的节点地址,因为4是下一组k个节点的起始地址,而1是当前组k个节点在交换完成后的最后一个 节点地址。
例如1、2、3、4、5、6,交换完一组k时,是3、2、1、4、5、6,交换第二组k(4、5、6)时,如果保存了节点4的地 址,即可以快速找到这一组节点的起始地址,而无需重头遍历;节点1之前是指向节点4的,之后需要指向节点6,所以 我们之前保存节点1的地址,方便这里更改next值。 - 当k=2时,4中的a不能用,例如1、2、3、4时,1的next值是2,2的next是3,如果互换next值,1的next是3,2的next值是2,所以在k=2时,需要做一个判断。其余与4相同。
代码:
时间上超出12%的用户
内存消耗上超出50%的用户
struct ListNode* reverseKGroup(struct ListNode* head, int k)
{
if (head == NULL || k == 1)
return head;
char first_k = 0;
struct ListNode* next_k = NULL; // 下一组K的起始节点
struct ListNode* before_next_k = NULL; // 前一组k的最后一个节点
struct ListNode* before_exchanged_node = NULL; //前一个节点的next需要更换
struct ListNode* before_after_node = NULL; //后者节点之前的节点next需要被更改
struct ListNode* node_in_k_befroe = head; //需要交换的前者
struct ListNode* node_in_k_after = NULL; //需要交换的后者
struct ListNode* temp_node = NULL; //前者节点之前的节点next需要被更改
int slow_ptr,fast_ptr;
int step = k - 1;
while (1) //标志位为1,表示当前组数据仍然需要执行
{
step = k - 1;
for (slow_ptr = 0; slow_ptr < k / 2; slow_ptr++) //慢指针位置,指示需要交换的前者指针
{
node_in_k_after = node_in_k_befroe;
for (fast_ptr = 0; fast_ptr < step; fast_ptr++)
{
if (fast_ptr == step - 2)
before_after_node = node_in_k_after->next; //记录后者节点之前的那一个节点,next也需要更换
//如果是两个的交换,就不需要
if (node_in_k_after->next == NULL)
return head; //如果是空节点,则不足k个,直接返回
else
node_in_k_after = node_in_k_after->next; //最后一次循环,找到需要交换的后节点
}
step -= 2;
if (!slow_ptr) //每组k的第一次循环
{
if (before_next_k != NULL)
{
before_next_k->next = node_in_k_after;
}
before_next_k = node_in_k_befroe; //记录当前组k在交换后的最后一个节点
next_k = node_in_k_after->next;
if (!first_k) //整个函数的第一次循环
{
first_k = 1;
head = node_in_k_after; //记录新头结点
}
}
temp_node = node_in_k_after->next; //暂存后者节点的next
if (k == 2) //后节点的next更改
{
node_in_k_after->next = node_in_k_befroe;
}
else
{
node_in_k_after->next = node_in_k_befroe->next;
}
node_in_k_befroe->next = temp_node; //前者节点的next更改
if (before_exchanged_node != NULL) //当前交换的节点,前者之前的那个节点的next更改
{
before_exchanged_node->next = node_in_k_after;
}
before_exchanged_node = node_in_k_after; //记录下一次要交换的前者节点之前那一个
if (before_after_node != NULL)
{
before_after_node->next = node_in_k_befroe; //当前交换的节点,后者之前那个节点的next需要更改
}
node_in_k_befroe = node_in_k_after->next;
}
before_exchanged_node = NULL;
before_after_node = NULL;
node_in_k_befroe = next_k;
if (node_in_k_befroe == NULL)
return head;
}
return head;
}