PAT Basic Level 1075 链表元素分类 解题思路及AC代码 v0.93

1. 题目简述及在线测试位置

1.1 给定一个链表 和 正数k,例如:3 2 -1 -2 0 1 和 k=1 。按如下规则对链表进行调整:规则1,负数需要排到最前面;规则2,大于k的数字需要排在[0,k] 数字的后面 ;规则3:每一类数字(指负数、大于k的数) 的原有顺序不改变,例如 之前是 -1 -2,调整后还是 -1 -2
1.2 在线测试位置: 1075 链表元素分类

2. 基本思路

2.0 本文采用了链表思路,实现起来真是一言难尽:具体实现不好想、 实现难度高、代码容易出错,debug了很久。最终在自己的努力下AC了,还是为自己点个赞。后面抽时间借鉴下别人的实现方式
2.1 数据结构采用结构体数组:数组下标对应链表结点的地址、数组元素包括:当前结点值、下一个结点的地址

struct LinkTable
{
	int Data;
	int Next;
}a[MAX];

2.2 首先按规则 负数需要排到最前面+负数的原有顺序不变 进行链表相关结点的调整。链表表头可能是负数,也可能非负数,因此我们需要先找到第一个非负数、即插入点,记录插入点、插入点的前一个结点,为后面的结点调整做准备。举个栗子:链表 -2 1 -3 4 ,插入位置应该是 -2 和 1之间,1是插入点,-2是插入点的前一个结点
另外需要注意,插入位置可能是头结点,需要单独处理。举个栗子:链表 1 -2 3,插入位置就是 1 的前面,插入-2后,头结点发生变化、插入位置也更新为 -2 和 1之间

int InsertPreNode = -1, InsertNode = HeadNode; //InsertPreNode = -1 表示插入结点是表头
	NowNode = HeadNode;
	//根据第一规则,定位插入点(链表中的第一个正数或0) //可能都是负数 需要添加循环退出条件
	while (a[NowNode].Data < 0 && NowNode != -1)
	{ 
		InsertPreNode = NowNode;
		InsertNode = a[NowNode].Next;

		PreNode = NowNode;
		NowNode = a[NowNode].Next;
	}

定位到插入结点后,按规则执行插入操作:表头(代码中是InsertPreNode=-1) 、非表头两种情况分开处理,将负数按原有顺序 都调整到链表的前面

	//根据第一规则调整链表
	//NowNode = HeadNode; //定位插入点操作后,不需要对NowNode重新赋值,因为插入点前的元素不需要执行插入操作;如果赋值,则是画蛇添足,链表头也会被弄丢
	while (NowNode != -1)
	{
		if (a[NowNode].Data >= 0)//非负数不调整
		{
			PreNode = NowNode;
			NowNode = a[NowNode].Next;		
		}			
		else //负数
		{
			a[PreNode].Next = a[NowNode].Next; //剥离当前结点

			if (InsertPreNode != -1) //插入位置非头结点
			{
				a[InsertPreNode].Next = NowNode;
				a[NowNode].Next = InsertNode;
				InsertPreNode = NowNode;
			}			
			else //插入位置在头结点
			{
				a[NowNode].Next = InsertNode;
				HeadNode = NowNode;
				InsertPreNode = NowNode;
			}
			NowNode = a[PreNode].Next;
		}		
	}

2.3 根据次规则进行调整 大于k的元素排在[0,k]的后面 + 大于k的元素原有位置不变,这个规则的难点是找到插入点。我的思路是 根据已有链表构造逆链表,顺序遍历逆链表,找到的第一个属于 [0,k]的数就是插入点 (原有的单向链表无法确认这个点,所以无奈之下构造逆链表)
例如:k=2, 3 4 2 0 1 7 9 插入位置就是 1 7之间

//通过逆链表b 找到符合第二规则的插入点
	NowNode = bHeadNode;
	PreNode = -1;
	while (NowNode != -1)
	{
		if (b[NowNode].Data <= Value) //第二规则的插入点
		{
			InsertPreNode = NowNode; 
			InsertNode = PreNode;
			break;
		}
		else
		{
			PreNode = NowNode;
			NowNode = b[NowNode].Next;
		}
	}

根据找到的插入点,执行插入操作即可。
注意,链表表尾插入 、非表尾插入分开处理。还需注意,如果退出第二规则插入点的查找循环时,若NowNode值为 -1,说明没有符合条件的插入点,也就是意味着不需要执行插入动作

	if (NowNode != -1) //测试点:找到了符合第二规则的插入点
	{
		//按第二规则调整 
		int Mark = InsertPreNode;
		NowNode = HeadNode; //原始链表的表头
		PreNode = -1; //与NowNode对应,代表其前的结点
		while (NowNode != Mark) //Mark及之后的结点不需要更改顺序
		{

			if (a[NowNode].Data <= Value)
			{
				PreNode = NowNode;
				NowNode = a[NowNode].Next;
			}
			else //需要调整的点 大于Value
			{
				if (PreNode == -1) //头结点需要改变位置
				{
					HeadNode = a[NowNode].Next; //剥离,头结点重新赋值

					a[InsertPreNode].Next = NowNode;
					a[NowNode].Next = InsertNode;
					InsertPreNode = a[InsertPreNode].Next; //遗漏了 导致bug2 节点丢失
					NowNode = HeadNode;
				}
				else //非头结点需要改变位置
				{
					a[PreNode].Next = a[NowNode].Next; //剥离

					a[InsertPreNode].Next = NowNode;
					a[NowNode].Next = InsertNode;
					//InsertNode = a[InsertPreNode].Next; //error
					InsertPreNode = a[InsertPreNode].Next;
					//6 11 插入到5 8中 5 6 8 ,再插入11是 5 6 11 8,改变的是InsertPreNode
					NowNode = a[PreNode].Next;
				}
			}
		}
	}
	//else if(NowNode == -1) //未找到符合第二规则的插入点 因此不动作

2.4 打完收工:思路可取,但是实现起来太容易出错、而且实现起来不容易,因此本解题方法不推荐 o(╯□╰)o

3. 完整AC代码

#include <iostream>
using namespace std;

#define MAX 100001

struct LinkTable
{
	int Data;
	int Next;
}a[MAX];

int main()
{	

	int HeadNode, NodeNumber, Value;
	cin >> HeadNode >> NodeNumber >> Value;

	int Address, Data, Next;
	for (int i = 0; i < NodeNumber; i++)
	{
		cin >> Address >> Data >> Next;
		a[Address].Data = Data;
		a[Address].Next = Next;
	}


	//当前节点、当前节点的前一个节点
	int NowNode, PreNode=-1; 
	//举栗子:在 4 6中插入5,6是InsertNode,4是InsertPreNode
	int InsertPreNode = -1, InsertNode = HeadNode; //InsertPreNode = -1 表示插入结点是表头
	NowNode = HeadNode;
	//根据第一规则,定位插入点(链表中的第一个正数或0) //可能都是负数 需要添加循环退出条件
	while (a[NowNode].Data < 0 && NowNode != -1)
	{ 
		InsertPreNode = NowNode;
		InsertNode = a[NowNode].Next;

		PreNode = NowNode;
		NowNode = a[NowNode].Next;
	}

	//根据第一规则调整链表
	//NowNode = HeadNode; //bug 这条语句造成此类链表死循环:-10 0 -18
	while (NowNode != -1)
	{
		if (a[NowNode].Data >= 0)//非负数不调整
		{
			PreNode = NowNode;
			NowNode = a[NowNode].Next;		
		}			
		else //负数
		{
			a[PreNode].Next = a[NowNode].Next; //剥离当前结点

			if (InsertPreNode != -1) //插入位置非头结点
			{
				a[InsertPreNode].Next = NowNode;
				a[NowNode].Next = InsertNode;
				InsertPreNode = NowNode;
			}			
			else //插入位置在头结点
			{
				a[NowNode].Next = InsertNode;
				HeadNode = NowNode;
				InsertPreNode = NowNode;
			}
			NowNode = a[PreNode].Next;
		}		
	}

	//通过构建逆链表b
	struct LinkTable b[MAX];
	b[HeadNode].Data = a[HeadNode].Data;
	b[HeadNode].Next = -1;

	NowNode = HeadNode;
	PreNode = NowNode;
	NowNode = a[NowNode].Next;
	while (NowNode != -1)
	{

		b[NowNode].Data = a[NowNode].Data;
		b[NowNode].Next = PreNode;

		PreNode = NowNode;
		NowNode = a[NowNode].Next;
	}
	int bHeadNode = PreNode;


	//通过逆链表b 找到符合第二规则的插入点
	NowNode = bHeadNode;
	PreNode = -1;
	while (NowNode != -1)
	{
		if (b[NowNode].Data <= Value) //第二规则的插入点
		{
			InsertPreNode = NowNode; 
			InsertNode = PreNode;
			break;
		}
		else
		{
			PreNode = NowNode;
			NowNode = b[NowNode].Next;
		}
	}

	if (NowNode != -1) //测试点:找到了符合第二规则的插入点
	{
		//按第二规则调整 
		int Mark = InsertPreNode;
		NowNode = HeadNode;
		PreNode = -1;
		while (NowNode != Mark)
		{

			if (a[NowNode].Data <= Value)
			{
				PreNode = NowNode;
				NowNode = a[NowNode].Next;
			}
			else //需要调整的点 大于Value
			{
				if (PreNode == -1) //头结点需要按第二规则调整
				{
					HeadNode = a[NowNode].Next;

					a[InsertPreNode].Next = NowNode;
					a[NowNode].Next = InsertNode;
					InsertPreNode = a[InsertPreNode].Next; //遗漏了 导致bug2 节点丢失

					NowNode = HeadNode;
				}
				else //非头结点需要按第二规则调整
				{
					a[PreNode].Next = a[NowNode].Next;

					a[InsertPreNode].Next = NowNode;
					a[NowNode].Next = InsertNode;
					//InsertNode = a[InsertPreNode].Next; //error
					InsertPreNode = a[InsertPreNode].Next;
					//6 插入到5 8中 5 6 8 ,再插入11是 5 6 11 8,改变的是InsertPreNode

					NowNode = a[PreNode].Next;
				}
			}
		}
	}
	//else if(NowNode == -1) //未找到符合第二规则的插入点 因此不动作

	NowNode = HeadNode;
	while (NowNode != -1)
	{

		printf("%05d %d ", NowNode,a[NowNode].Data);

		if (a[NowNode].Next == -1)
			printf("-1\n");
		else
			printf("%05d\n", a[NowNode].Next);

		NowNode = a[NowNode].Next;
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这道题目要求我们将一个链表中的元素按照奇偶性分成两个链表,其中奇数链表中的元素顺序与原链表中的顺序相同,偶数链表中的元素顺序与原链表中的顺序相反。 我们可以使用两个指针分别指向奇数链表和偶数链表的尾部,然后遍历原链表,将奇数节点插入奇数链表的尾部,偶数节点插入偶数链表的头部。最后将偶数链表反转即可。 具体实现可以参考以下代码: ```cpp #include <iostream> #include <vector> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; ListNode* createList(vector<int>& nums) { ListNode* head = new ListNode(0); ListNode* p = head; for (int num : nums) { p->next = new ListNode(num); p = p->next; } return head->next; } void printList(ListNode* head) { while (head) { cout << head->val << " "; head = head->next; } cout << endl; } ListNode* oddEvenList(ListNode* head) { if (!head || !head->next) return head; ListNode* oddTail = head; ListNode* evenHead = head->next; ListNode* evenTail = evenHead; while (evenTail && evenTail->next) { oddTail->next = evenTail->next; oddTail = oddTail->next; evenTail->next = oddTail->next; evenTail = evenTail->next; } oddTail->next = evenHead; ListNode* p = evenHead; ListNode* q = NULL; while (p) { ListNode* tmp = p->next; p->next = q; q = p; p = tmp; } evenHead = q; oddTail->next = evenHead; return head; } int main() { vector<int> nums = {1, 2, 3, 4, 5}; ListNode* head = createList(nums); printList(head); head = oddEvenList(head); printList(head); return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值