问题地址:
https://pta.patest.cn/pta/test/1342/exam/4/question/19210
本题的原意是希望: 用数组,模拟在连续内存中,原地实现反转链接:
#include <stdio.h>
#define MAX 100000
typedef struct{
int data;
int next;
}Node;
//计算并返回单链表节点数
int CountNodes(Node* list, int pList) {
int cnt = 1;
while((pList = list[pList].next) != -1)
cnt++;
return cnt;
}
int ReverseK(Node* list, int pList, int n, int k) {
int prevNode, currNode, nextNode;
//需要连接的前一个节点、当前节点、后一个节点
prevNode = -1;
currNode = pList;
nextNode = list[currNode].next;
int lastHead, head = -1;
for(int i = 0; i < n / k; i++){ //分为n/k段分别逆转,每段k个节点
lastHead = head; //记录前一段的(未逆转的)头结点,以便连接到当前段的(未逆转的)尾节点
head = currNode; //记录当前段的头结点
for(int j = 0; j < k; j++) { //k个节点逆转:后一个节点指向前一个节点
list[currNode].next = prevNode;
prevNode = currNode;
currNode = nextNode;
nextNode = list[nextNode].next;
}
if(i == 0) //第一段逆转后的头结点将作为表头返回
pList = prevNode;
else //连接逆转后的前后两段
list[lastHead].next = prevNode;
}
list[head].next = currNode; //将不用逆转的剩余部分连接到逆转链表尾部
return pList;
}
void printList(Node* list, int p) {
while(list[p].next != -1){
printf("%05d %d %05d\n", p, list[p].data, list[p].next);
p = list[p].next;
}
printf("%05d %d %d\n", p, list[p].data, list[p].next);
}
int main() {
int pList, n, k;
scanf("%d%d%d", &pList, &n, &k);
Node list[MAX];
int addr, data, next;
for(int i = 0; i < n; i++){
scanf("%d%d%d", &addr, &data, &next);
list[addr].data = data;
list[addr].next = next;
}
int num = CountNodes(list, pList); //因输入中有无效的节点,需要先计算单链表中的总结点数
int pNewList = ReverseK(list, pList, num, k);
printList(list, pNewList);
return 0;
}
自己最初的代码,属于老师说的取巧方法,排成有序链表,模拟栈弹出,时间效率低下,实现过程中审题不仔细,踩坑极多。
// 02_Reversing Linked List.cpp
#include "stdio.h"
#include "stdlib.h"
typedef struct ListNode ListNode, *LinkedList;
struct ListNode {
int Address;
int Data;
int NextAddress; // 当为-1时,表示NULL,即为list尾
LinkedList NextNode;
};
int totalNodes = 0;
void PrintNode(LinkedList pNode){
int addr = (pNode)->Address;
int data = (pNode)->Data;
int NextAddress = (pNode)->NextAddress;
printf("%05d %d ", addr, data);
if(NextAddress == -1){
printf("%d\n", NextAddress);
}else{
printf("%05d\n", NextAddress);
}
}
void PrintList(LinkedList pList){
LinkedList tmp = pList;
while(totalNodes--){
PrintNode(tmp);
tmp = tmp->NextNode;
}
}
LinkedList PopNodeByAddress(int addr, LinkedList *pList){
//ruturn 目标node的物理地址(指针)
LinkedList targetNode, tmp;
tmp = *pList;
while(tmp->NextNode){
if(addr != tmp->NextNode->Address){
tmp = tmp->NextNode;
}else{
targetNode = tmp->NextNode; //找到了targetNode
tmp->NextNode = targetNode->NextNode; //将targetNode前后连接起来
}
}
return targetNode;
}
LinkedList PopNodeFromHead(LinkedList *pList){
//ruturn 目标node的物理地址(指针)
LinkedList targetNode;
targetNode = *pList; //链首位即为targetNode
*pList = (*pList)->NextNode;
return targetNode;
}
void AddNode(int addr, int data, int nextAddr, LinkedList *pList){
LinkedList node = (LinkedList)malloc(sizeof(struct ListNode));
node->Address = addr;
node->Data = data;
node->NextAddress = nextAddr;
node->NextNode = NULL;
(*pList)->NextNode = node;
*pList = node;
}
//读取全部数据进list
LinkedList ReadList(int firstAddr, int numOfNode){
int addr, data, nextAddr, nodeCount;
LinkedList head, tail; //head主要用来记录空节点并释放
//先读取输入的list
LinkedList inputList = (LinkedList)malloc(sizeof(struct ListNode));
inputList->NextNode = NULL; //生成一个空head,方便attach
tail = inputList;
nodeCount = numOfNode;
while(nodeCount--){
scanf("%d %d %d", &addr, &data, &nextAddr);
AddNode(addr, data, nextAddr, &tail); //在tail添加node,并更新tail
}
//此时还需保留空head
//将输入的list依Address排序,重新链接
LinkedList orderList = (LinkedList)malloc(sizeof(struct ListNode));
orderList->NextNode = NULL; //首位空head,赋NextAddress值便于查找链接
orderList->NextAddress = firstAddr;
head = orderList;
tail = orderList;
int count = numOfNode;
//当tail.NextAddress == -1 说明已到终点
while(tail->NextAddress != -1){
nextAddr = tail->NextAddress;
tail->NextNode = PopNodeByAddress(nextAddr, &inputList);
tail = tail->NextNode;
totalNodes += 1;
}
tail->NextAddress = -1;
tail->NextNode = NULL;
orderList = orderList->NextNode;
// free(inputList); //释放inputList仅存的首位空head
// free(tail); //释放记录orderList的tail
// free(head); //释放记录orderList的首位空head
return orderList;
}
LinkedList ReverseList(int numOfNode, int lengthOfSubList, LinkedList pList){
LinkedList reverseList = (LinkedList)malloc(sizeof(struct ListNode));
reverseList->NextNode = NULL; //首位空head,赋NextAddress值便于查找链接
LinkedList tail = reverseList;
int countPrint = totalNodes/lengthOfSubList;
int countLink = totalNodes;
while(countPrint--){
LinkedList data[lengthOfSubList];
for(int count = 0; count<lengthOfSubList; count++){
LinkedList node = PopNodeFromHead(&pList);
data[count] = node;
}
for(int count = lengthOfSubList-1; count>-1; count--){
tail->NextNode = data[count];
tail->NextAddress = tail->NextNode->Address; //反转的时候需要修改nextAddress!!!--- 坑!!!!
tail = tail->NextNode;
countLink--;
}
}
// 若未反转完,保留后面的list不翻转,但需要更新NextAddress
if(countLink != 0){
tail->NextNode = pList;
tail->NextAddress = tail->NextNode->Address;
}else{
tail->NextAddress = -1;
}
reverseList = reverseList->NextNode;
return reverseList;
}
int main(){
LinkedList orderList, revList;
int firstAddr, numOfNode, lengthOfSubList;
scanf("%d %d %d", &firstAddr, &numOfNode, &lengthOfSubList);
orderList = ReadList(firstAddr, numOfNode);
// PrintList(orderList);
revList = ReverseList(numOfNode, lengthOfSubList, orderList);
PrintList(revList);
return 0;
}