2018大华软件校园创新挑战赛-翻转指定元素链表

本文介绍了一种算法,该算法能够实现链表中每K个节点的翻转操作,包括不足K个节点的最后一组。通过定义一个List类,并在其中实现reverse函数,可以有效地完成这一任务。

题目:

给定一个链表,实现一个函数,将链表中每k个节点进行翻转,若最后一组节点数量不足k个,则按实际个数翻转。例如:给定链表1->2->3->4->5->6->7->8->NULL,k=3,翻转后输出3->2->1->6->5->4->8->7->NULL。

翻转函数reverse有两个参数,分别为链表头指针和翻转步长k。

输入:

输入的第一行指定用例数量T;
用例的第一行输入指定链表节点个数N;
用例的第二行输入指定N个节点数值,使用空格隔开;

用例的第三行输入指定翻转步长K;

输出:

按照K翻转后的链表节点值

例如:

Input:
1
8
1 2 2 4 5 6 7 8
4


Output:

4 2 2 1 8 7 6 5

解题思路:

    调用我之前写过的单链表数据结构来搭建链表:数据结构-单链表-C++模板实现

    有关题目翻转的操作放在List类reverse()函数中,大概思路是指针指向需要翻转的头和尾,调换头尾的顺序,然后头指针++,尾指针--,直到头>尾表示这段已经翻转完毕,具体解释代码中有。

#include <iostream>
using namespace std;

template<class T>
class Node
{
public:
	T data;
	Node<T> * next;

	Node() :next(NULL) {}

	Node(T value) :next(NULL), data(value) {}

	~Node() {}

	friend ostream& operator<<(ostream &os, const Node<T> *node)
	{
		os << node->data;
		return os;
	}
};

template<class T>
class List
{
public:
	List()
	{
		m_pList = new Node<T>;
		m_iLength = 0;
	}

	~List()
	{
		ClearList();
		delete m_pList;
	}

	void ClearList()
	{
		for (int i = 0; i<m_iLength; i++)
			ListDelete(i);

		m_pList->next = NULL;
		m_iLength = 0;
	}

	bool ListEmpty()
	{
		return !m_iLength;
	}

	int ListLength()
	{
		return m_iLength;
	}

	bool ListInsert(int i, T value)//将value插入第i个节点之前
	{
		if (i<0 || i>m_iLength) return false;

		//先找到需要插入的i节点之前的节点currentNodeBefore  
		Node<T> *currentNodeBefore = m_pList;
		for (int k = 0; k<i; k++)
			currentNodeBefore = currentNodeBefore->next;

		//new一个Node  
		Node<T> * newNode = new Node<T>(value);

		//将newNode插入currentNodeBefore之后,即为插入i节点前  
		newNode->next = currentNodeBefore->next;
		currentNodeBefore->next = newNode;

		m_iLength++;
		return true;
	}

	bool ListDelete(int i)//删除第i个节点  
	{
		if (i<0 || i >= m_iLength) return false;

		//找到需要删除节点的前一个节点  
		Node<T> *currentNodeBefore = m_pList;
		for (int k = 0; k < i; k++)
			currentNodeBefore = currentNodeBefore->next;

		//定位currentNode,删除操作  
		Node<T> *currentNode = currentNodeBefore->next;
		currentNodeBefore->next = currentNodeBefore->next->next;

		delete currentNode;

		m_iLength--;
		return true;
	}


	bool ListInsertTail(T value)
	{
		//new一个Node,将newNode->next置为NULL  
		Node<T> *newNode = new Node<T>(value);


		//找到尾节点currentNode  
		Node<T> *currentNode = m_pList;
		while (currentNode->next != NULL)
			currentNode = currentNode->next;

		//将newNode连接上尾节点currentNode  
		currentNode->next = newNode;

		m_iLength++;
		if (newNode->next == NULL)
			return true;
		else return false;
	}

	T& GetElem(int i)//返回第i节点数据 
	{
		Node<T> *currentNode = m_pList->next;
		for (int k = 0; k < i; k++)
			currentNode = currentNode->next;

		return currentNode->data;
	}

	void reverse(int k)
	{
		int reverseTime = m_iLength / k;//翻转的整段数,eg:1 2 3 4 5 6 7 8,k=3,则123为一段,456为一段,需要翻转的整段数为2
		int pre = 0;//用来指向需要翻转段的头节点,闭区间
		int cur = k - 1;//用来指向需要翻转段的尾节点,闭区间

		//处理整段的翻转
		for (int i = 0; i < reverseTime; i++)
		{
			int ppre = pre;
			int pcur = cur;
			while (ppre < pcur)
			{
				int preData = GetElem(ppre);
				int curData = GetElem(pcur);
				ListDelete(pcur);
				ListInsert(pcur, preData);
				ListDelete(ppre);
				ListInsert(ppre, curData);

				ppre++;
				pcur--;
			}

			pre += k;
			cur += k;
		}

		//当整段翻转完毕,pre指向下一段的开头,cur肯定超出边界,所以将cur拉回至末尾元素
		while (cur >= m_iLength)
			cur--;

		//当整段翻转完成后恰好到末尾,后面没有需要翻转的元素了,则退出
		if (pre >= cur)
			return;

		//处理尾端不完整段的翻转
		while (pre < cur)
		{
			int preData = GetElem(pre);
			int curData = GetElem(cur);
			ListDelete(cur);
			ListInsert(cur, preData);
			ListDelete(pre);
			ListInsert(pre, curData);

			pre++;
			cur--;
		}

	}

	void ListTraverse()
	{
		Node<T> *currentNode = m_pList->next;
		while (currentNode != NULL)
		{
			cout << currentNode << " ";
			currentNode = currentNode->next;
		}
		cout << endl;
	}


public:
	Node<T> *m_pList;
	int m_iLength;
};

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int listSize;
		cin >> listSize;

		List<int> list;
		for (int i = 0; i < listSize; i++)
		{
			int node;
			cin >> node;
			list.ListInsertTail(node);
		}

		int reverseNum;
		cin >> reverseNum;
		list.reverse(reverseNum);
		list.ListTraverse();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值