25、K个一组翻转链表

作为一个菜鸟,最近在看《我的第一本算法书》,想在之前薄弱的基础上,多了解一点。虽然目前的工作重心不在算法上,但是多会一点总不是什么坏事

《我的第一本算法书》很通俗易懂,配了大量直白的图片,让我能够以很快的速度去了解之前没接触过的知识点, 但问题也在于过于通俗易懂了,跟看连环画一样,容易遗忘。因此在翻了一部分之后,回到前面,根据每一节的知识点,到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
 

解题思路:

  1. 根据说明,知道需要更改的是每一个节点的next值,每个节点中的val值不能做任何改变;
  2. 图一为一开始时的链表。
  3. 当k值为1时,直接返回头结点即可。
  4. 当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值。
  5. 当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;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值