PAT数据结构_02-线性结构3 Reversing Linked List (25分)

问题地址:

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;

}





题目描述 给定一个常数 $K$ 以及一个单链表 $L$,请编写程序将 $L$ 中每 $K$ 个结点反转。例如:给定 $L$ 为 1→2→3→4→5→6,$K$ 为 3,则输出应该为 3→2→1→6→5→4;如果 $K$ 为 4,则输出应该为 4→3→2→1→5→6,即最后不到 $K$ 个元素不反转。 输入格式 每个输入包含一个测试用例。每个测试用例第 1 行给出第 1 个结点的地址、结点总个数正整数 $N (\le 10^5)$、以及正整数 $K (\le N)$,即要求反转的子链结点的个数。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。 接下来有 $N$ 行,每行格式为: Address Data Next 其中 Address 是结点地址;Data 是该结点保存的整数数据;Next 是下一结点的地址。题目保证给出的链表不为空。 输出格式 对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。 输入样例 00100 6 4 00000 4 99999 00100 1 12309 68237 6 -1 33218 3 00000 99999 5 68237 23333 2 33218 输出样例 00000 4 33218 33218 3 12309 12309 1 99999 99999 5 68237 68237 6 23333 23333 2 -1 题目析 本题需要将链表中每 $K$ 个结点反转,可以采用迭代的方法,每次找到 $K$ 个结点,将这 $K$ 个结点反转,然后将这 $K$ 个结点的前驱结点指向反转后的第一个结点,将反转后的 $K$ 个结点的最后一个结点指向下一个要反转的结点,然后继续进行下一轮反转。 需要注意的是,如果链表长度不足 $K$,则不进行反转。 代码实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值