leecode 解题总结:25 Reverse Nodes in k-Group

本文主要介绍了LeetCode中25题的解题思路,即给定一个链表,每次按k个节点进行逆序操作,返回修改后的链表。解题过程中详细阐述了如何利用常量空间实现链表的k逆序分组,涉及链表反转和分组连接的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <iostream>
#include <stdio.h>
#include <vector>

using namespace std;
/*
问题:
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.

k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.

You may not alter the values in the nodes, only nodes itself may be changed.

Only constant memory is allowed.

For example,
Given this linked list: 1->2->3->4->5

For k = 2, you should return: 2->1->4->3->5

For k = 3, you should return: 3->2->1->4->5

分析:
a multiple of k:k的倍数
注意:k的倍数内的结点反转,剩余的部分保持不变。因此有点类似于:
1~k反转,k+1~2k反转。那就需要将结点分成长度为k的组,每一组组内
反转,除了首结点没有前驱外,其余结点找到前驱结点,令当前结点指向前驱结点即可。
关键只允许常量空间

此题主要是将从1~k的结点全部反转,后续结点不发生改变。只需要找到第k+1个结点,
然后反转1~k的结点,并令反转后链表的末尾结点指向第k+1个结点。
关键在于如何将1~k的结点进行反转。
可以保存当前结点q的前驱结点p以及当前结点q的下一个结点n,
然后用q指向p即可

输入:
5(元素个数) 2(k)
1 2 3 4 5(链表元素)

5 3
1 2 3 4 5

5 1
1 2 3 4 5

5 5
1 2 3 4 5

8 3
1 2 3 4 5 6 7 8

8 4
1 2 3 4 5 6 7 8

1 2
1

输出:
2 1 4 3 5
3 2 1 4 5
1 2 3 4 5
5 4 3 2 1
3 2 1 6 5 4 7 8
4 3 2 1 8 7 6 5

关键:
1报错:
Runtime Error Message:
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 18446744073709551615) >= this->size() (which is 1)
Last executed input:
[1]
2

漏了一种情况:
//无需反转
if(nodeNum < k)
{
	return head;
}
2//对整个数组的结点进行反转,反转后,数组最后一个元素是起始结点,第一个元素是结束结点
void reverseNode( vector<ListNode*>& nodes )
{
	if(nodes.empty())
	{
		return ;
	}
	int size = nodes.size();
	if(1 == size)
	{
		return;
	}
	else if(2 == size)
	{
		nodes.at(1)->next = nodes.at(0);
	}
	else
	{
		ListNode* node , *nextNode , *previousNode;
		previousNode = nodes.at(0);
		node = nodes.at(1);
		nextNode = nodes.at(2);
		int size = nodes.size();
		int count = 3;//这里是从nextNode记数,所以是3
		while(count < size)
		{
			//设置当前结点指向前驱结点,并令当前结点
			node->next = previousNode;

			//更新结点
			previousNode = node;
			node = nextNode;
			nextNode = nextNode->next;
			count++;
		}
		//最后设置最后结点指向
		nextNode->next = node;
		node->next = previousNode;
	}
}
*/

struct ListNode {
     int val;
     ListNode *next;
     ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
	int getLength(ListNode* head)
	{
		int len = 0;
		while(head)
		{
			len++;
			head = head->next;
		}
		return len;
	}

	//对整个数组的结点进行反转,反转后,数组最后一个元素是起始结点,第一个元素是结束结点
	void reverseNode( vector<ListNode*>& nodes )
	{
		if(nodes.empty())
		{
			return ;
		}
		int size = nodes.size();
		/*
		vector<ListNode*> copyNodes(nodes);//复制一份结点,不能使用一份结点
		for(int i = 1 ; i < size ; i++)
		{
			nodes.at(i)->next = copyNodes.at(i-1);
		}
		*/
		if(1 == size)
		{
			return;
		}
		else if(2 == size)
		{
			nodes.at(1)->next = nodes.at(0);
		}
		else
		{
			ListNode* node , *nextNode , *previousNode;
			previousNode = nodes.at(0);
			node = nodes.at(1);
			nextNode = nodes.at(2);
			int size = nodes.size();
			int count = 3;//这里是从nextNode记数,所以是3
			while(count < size)
			{
				//设置当前结点指向前驱结点,并令当前结点
				node->next = previousNode;

				//更新结点
				previousNode = node;
				node = nextNode;
				nextNode = nextNode->next;
				count++;
			}
			//最后设置最后结点指向
			nextNode->next = node;
			node->next = previousNode;
		}
	}

    ListNode* reverseKGroup(ListNode* head, int k) {
        if(NULL == head || k <= 0)
		{
			return NULL;
		}
		if(1 == k )
		{
			return head;
		}
		int nodeNum = getLength(head);
		//无需反转
		if(nodeNum < k)
		{
			return head;
		}
		int groupNum = (int)ceil(nodeNum * 1.0 / k ) ;//向上取整
		vector< vector<ListNode*> > groupNodes;
		for(int i = 0 ; i < groupNum ; i++)
		{
			vector<ListNode*> nodes;
			groupNodes.push_back(nodes);
		}

		//获取结点个数,进行分组
		int count = 0;
		while(head)
		{		
			groupNodes.at( ( count / k ) ).push_back(head);
			count++;
			head = head->next;
		}

		//分组后,接下来,交换组内所有结点,需要记录结点的前驱结点
		vector<ListNode*> nodes;
		ListNode* previousNode = NULL;
		ListNode* node = NULL;
		ListNode* nextNode = NULL;
		int reverseGroupNum = groupNum;
		//判断是否组内有剩余,有剩余结点的一组不进行反转
		if( nodeNum % k != 0 )
		{
			reverseGroupNum--;
		}
		for(int i = 0 ; i < reverseGroupNum ; i++)
		{
			nodes = groupNodes.at( (i ) );
			reverseNode(nodes);
		}
		//所有反转后,组外结点进行连接,当前组的头结点(链表尾部)连接到下一组的尾结点(链表首部)
		for(int i = 0; i < reverseGroupNum - 1; i++)
		{
			int nodeSize = groupNodes.at(i+1).size();
			groupNodes.at(i).at(0)->next = groupNodes.at(i+1).at( nodeSize - 1 );
		}
		//恰好分组后,最后一组的尾结点需要指向空结点
		if( nodeNum % k == 0 )
		{
			groupNodes.at( groupNum - 1).at(0)->next = NULL;
		}
		//某组有剩余,倒数第二组结点指向最后一组头结点
		else
		{
			groupNodes.at(reverseGroupNum - 1).at(0)->next = groupNodes.at(reverseGroupNum).at(0);
		}
		return groupNodes.at(0).at( groupNodes.at(0).size() - 1 );
    }
};

ListNode* buildList(vector<int>& datas)
{
	if(datas.empty())
	{
		return NULL;
	}
	ListNode* head;
	ListNode* tail;
	int size = datas.size();
	for(int i = 0 ; i < size ; i++)
	{
		if(i)
		{
			ListNode* node = new ListNode(datas.at(i));
			tail->next = node;
			tail = node;
		}
		else
		{
			head = new ListNode(datas.at(i));
			tail = head;
		}
	}
	return head;
}

void releaseList(ListNode* head)
{
	while(head)
	{
		ListNode* nextNode = head->next;
		delete head ;
		head = nextNode;
	}
}

void print(ListNode* head)
{
	while(head)
	{
		cout << head->val << " ";
		head = head->next;
	}
	cout << endl;
}

void process()
{
	int nodeNum;
	vector<int> datas;
	int value;
	Solution solution;
	int k;
	while(cin >> nodeNum >> k)
	{
		datas.clear();
		for(int i = 0 ; i < nodeNum ; i++)
		{
			cin >> value;
			datas.push_back(value);
		}
		ListNode* head = buildList(datas);
		ListNode* newHead = solution.reverseKGroup(head , k);
		print(newHead);
		releaseList(newHead);
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值