《数据结构》02-线性结构3 Reversing Linked List

PTA的一道翻转链表题

题目详情 (pintia.cn)

没看题解前的思路与总结

  1. 根据题目样例,想到了模拟内存,但因为想节约空间(利用动态数组)模拟的不完全,把地址放进了结构体中(Ps:虽然代码还是没节约空间,有点懒)。由于设计的数据结构的缘故,算法时间复杂度为O(^{^{}}{N^{2}}),数据量最大的例子超时了
  2.  翻转的策略选择了:不断的交换两个节点的位置。采用了数组翻转的思路(忽略了单链表有一定规律,只需依次逆向节点的指向即可
  3. 最后输出的时侯如何输出如:00010,这样的数字遇到了点困难,本考虑用字符数组存储字符串的形式,但在更改指向时由于字符数组为const的指针,逐放弃。
  4. 此外没有引入一个哑头节点,导致处理起来有点复杂。

#include <stdio.h>
#define N 100000
//"模拟"内存中的单链表节点
//逻辑上相连,物理地址上不相连
struct Node {
	int address;
	int data;
	int next;
};
struct Node List[N];
int node_numbers = 0;//链表的节点数
void Read(int n);
int  Reverse(int first_address, int length);
void Printf(int address);
int Find(int address);
int Find_last(int address);//找到节点的前驱(单链表的局限性)
int main() {
	int first_address = 0;
	int reverse_length = 0;
	scanf("%d %d %d", &first_address, &node_numbers, &reverse_length);
	int new_first_address =first_address;
	Read(node_numbers);
	if (reverse_length!=1)
	{
		 new_first_address=Reverse(first_address,reverse_length);

	}
	Printf(new_first_address);
}
void Read(int n)
{
	for (int i = 0; i < n; i++) {
		scanf("%d %d %d", &List[i].address, &List[i].data, &List[i].next);
	}
	return;
}
int  Reverse(int first_address,  int length)
{
	int count = node_numbers / length;	//计算翻转的次数
	int  first=0;//头节点
	int new_first_address = 0;//翻转后的链表头节点地址

	//找到第一个节点
	first = Find(first_address);

	//翻转:通过不断的调换m,n的位置
	//因为是单链表,所以需要m,n前一个节点的位置
	int m = first;//m在前
	int n = first;//n在后
	int n_last = 0;
	int m_last = 0;
	int temp = 0;//临时记录
	int read = 0;//记录下段翻转的m

	//判断是否要翻转
	if (count)
	{
		//第一段翻转的第一次交换(特殊性:头节点没有前节点)
		//找到n
		for (int i = 1; i < length; i++) {
			n = Find(List[n].next);
		}
		 n_last = Find_last(List[n].address);

		// 记录翻转完后整个链表新的头节点
		new_first_address = List[n].address;

		//记录下段翻转的m
		 read = Find(List[n].next);

		 //length为2时的情况
		 if (m==n_last)
		 {
			 List[m].next = List[n].next;
			 List[n].next = List[m].address;
			 //一次交换完后就完成了该段的翻转,进入下一段翻转
			 m_last = m;
			 m = read;
			 n = Find(List[m].next);
			 n_last = m;
			 count--;
		 }
		 else
		 {
			 temp = List[n].next;
			 List[n].next = List[m].next;
			 List[n_last].next = List[m].address;
			 List[m].next = temp;

			 m_last = n;
			 m = Find(List[m_last].next);
			 n = n_last;
			 n_last = Find_last(List[n].address);
		 }
		

	
	}
	
	while (1)
	{

		//一个长度内的翻转
		while (1)
		{

			//交换结束的条件
			//1.m,n相临
			if (m == n_last)
			{
				List[m].next = List[n].next;
				List[n].next = List[m].address;
				List[m_last].next = List[n].address;
				break;
			}
			//2.m,n相等
			if (m == n)
			{
				break;
			}
			//在链表逻辑中交换m,n节点
			temp = List[n].next;
			List[n].next = List[m].next;
			List[m_last].next = List[n].address;
			List[n_last].next = List[m].address;
			List[m].next = temp;
			//更新m,n以及它们的前一个节点
			m_last = n;
			m = Find(List[m_last].next);
			n = n_last;
			n_last = Find_last(List[n].address);

		}
			count--;
		//进入下一段翻转,重新找到m,n
		//如果read为-1或者count为0,则已完成整个链表的翻转
		if (read==-1||count==0)
		{
			break;
		}
		m = read;
		m_last = Find_last(List[m].address);
		n = m;
		//找到n
		for (int i = 1; i < length; i++) {
			//防止输入中有“多余”的节点 
			if(n==-1){
				break;
			}
			n_last = n;
			n = Find(List[n].next);
		}
		//防止输入中有“多余”的节点 
			if(n==-1){
				break;
			}
		//记录下段翻转的m
		read = Find(List[n].next);
	}
		
	return new_first_address;

}
void Printf(int address)
{
	int i = 0;
	while (1)
	{
		i = Find(address);
		if(List[i].next!=-1){
			printf("%05d %d %05d\n", List[i].address, List[i].data, List[i].next);
		}else{
			printf("%05d %d -1\n", List[i].address, List[i].data);
			break;
		}
		address = List[i].next;
	}
	return;
}
int Find(int address)
{
	//如果是最后一个节点
	if (address == -1)
	{
		return -1;
	}
	//遍历查找 
	int result = 0;
	for (int i = 0; i < node_numbers; i++) {
		if (List[i].address == address) {
			result = i;
			break;
		}
	}
	return result;
}

int Find_last(int address)
{
	//遍历查找 
	int result = 0;
	for (int i = 0; i < node_numbers; i++) {
		if (List[i].next == address) {
			result = i;
			break;
		}
	}
	return result;

}

 

 

看完题解后的笔记与总结

题解:数据结构_中国大学MOOC(慕课) (icourse163.org)

  1. 设计数据结构时应模拟一个真实的链表在内存里面存在的状态,开一个充分大的结构数组来代表内存空间,数组下标表示地址。
  2. 只在完全没规律的时侯通过交换结点来更改链表的结构如:排序。
#include<stdio.h>
#define N 100000
struct Node
{
	int data;
	int next;
};
struct Node List[N];
void Read(int numbers);
void Printf(int first);
int Reverse(int first, int length);
int main()
{
	int first = 0;
	int numbers = 0;
	int length = 0;
	scanf("%d %d %d", &first, &numbers, &length);
	 Read(numbers);
	 int new_first = first;
	 if (length!=1)
	 {
		 new_first=Reverse(first,length);
	}
	 Printf(new_first);
	 return 0;
}

void Read(int numbers)
{
	int address = 0;
	for (int i = 0; i < numbers; i++) {
		scanf("%d ", &address);
		scanf("%d %d", &List[address].data, &List[address].next);
	}
	return;
}

void Printf(int first)
{
	int address = first;
	while (List[address].next!=-1)
	{
		printf("%05d %d %05d\n", address, List[address].data, List[address].next);
		address = List[address].next;
	}
	printf("%05d %d -1", address, List[address].data);
	return;
}

int Reverse(int first, int length)
{
	int node_numbers = 1;//防止有多余的节点不在链表上
	int address = first;
	while (List[address].next!=-1)
	{
		node_numbers++;
		address = List[address].next;
	}
	int count = node_numbers / length;//需要逆序的段数

	struct Node head;//哑头节点
	struct Node* point_head =&head;
	point_head->next = first;
	if (count)
	{
		//找到新的头节点
		for (int i = 1; i < length; i++)
		{
			first = List[first].next;
		}
	}

	int pre = 0;
	int old = 0;
	int temp = 0;
	//翻转
	while (count>0)
	{
		 pre = point_head->next;
		 old = List[pre].next;
		 temp = List[old].next;
		 //一段内的逆向
		 for (int i = 1; i < length; i++)
		 {
			 List[old].next = pre;
			 pre = old;
			 old = temp;
			 if (temp!=-1)
			 {
			  temp = List[temp].next;
			  }
		 }
		 List[point_head->next].next = old;
		 temp = point_head->next;
		 point_head->next = pre;
		 point_head = &List[temp];
		 count--;
	}

	return first;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值