Reversing Linked List

問題描述

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.

Input Specification:
Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤105?? ) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.
Then N lines follow, each describes a node in the format:
Address Data Next

where Address is the position of the node, Data is an integer, and Next is the position of the next node.

Output Specification:
For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.

Sample Input:
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
Sample Output:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1

題意便是把鏈表的每K個節(jié)點(diǎn)反轉(zhuǎn),如果最后結(jié)尾的節(jié)點(diǎn)數(shù)小于K,便不需要反轉(zhuǎn)
頭節(jié)點(diǎn)格式:
[第一個數(shù)據(jù)的節(jié)點(diǎn)地址 數(shù)據(jù)節(jié)點(diǎn)數(shù) 反轉(zhuǎn)數(shù)K]
[address N K]

其他節(jié)點(diǎn)內(nèi)容的格式為:
[本節(jié)點(diǎn)地址 數(shù)據(jù) 下一個節(jié)點(diǎn)地址]
[address data next]

Notice:如果下一個節(jié)點(diǎn)地址(next)為-1相當(dāng)于null,表示這是最后一個節(jié)點(diǎn)

具體如圖所示:
由上方鏈表轉(zhuǎn)為下方鏈表(K == 3)


image.png

解決思路

Tip:后面所說的函數(shù)都是代碼里面的函數(shù)

正文:

由示例可知,題目給出數(shù)據(jù)時(shí),節(jié)點(diǎn)是雜亂無章的。首先做的第一件事便是將之排序成為一個有序的鏈表。

這一步我的思路便是使用一個鏈表來接受這些數(shù)據(jù),然后把它們排序之后放入一個隊(duì)列K
使用createList() 把題目數(shù)據(jù)一股腦放到一個鏈表之中(無序)
使用putListToQueue()將那些數(shù)據(jù)按照地址順序排好放在一個隊(duì)列K之中

這里可能會有一個疑問,這里為什么使用隊(duì)列?直接在鏈表上完成整個操作不可以嗎?

直接在鏈表操作確實(shí)可以,但是!其中涉及Node的排序,反轉(zhuǎn)的操作便會復(fù)雜些!而鏈表反轉(zhuǎn)是符合棧(First in Last Out)的特性的!而且最后一個測試點(diǎn)還有一個坑?。。ê竺嬖僬f)使用鏈表個人會覺得變得麻煩許多,不利于思維的通暢

使用隊(duì)列我們稍微改造一番也可以讓隊(duì)列具有的特性。而按順序輸出便完全符合隊(duì)列的特性(First in Last out),所以只使用隊(duì)列完全可以符合我們的反轉(zhuǎn),輸出要求

好了,現(xiàn)在我們已經(jīng)得到一個排序好的隊(duì)列K了

下一步我們只需使reserveQueue()隊(duì)列K反轉(zhuǎn)到另一個隊(duì)列Q,然后把隊(duì)列Q輸出即可

Notice!使用reserveQueue()是把隊(duì)列K元素pop到隊(duì)列Q來完成反轉(zhuǎn)的的!
也就是說我們把隊(duì)列K每K個元素以的方式pop到隊(duì)列Q之中,從而得到了一個已經(jīng)反轉(zhuǎn)好的隊(duì)列!
還有一點(diǎn)!隊(duì)列K我們只是把每K個節(jié)點(diǎn)用的方式輸出,但是我們把每K個節(jié)點(diǎn)(結(jié)尾不管夠不夠K個都算是一個節(jié)點(diǎn))看成一個大的節(jié)點(diǎn)是按照隊(duì)列的方式輸出的!也就是說,局部以棧的方式pop,整體以隊(duì)列的方式pop

最后使用printResult()將結(jié)果輸出即可

Some Detail Of Function (里面有一些寫題時(shí)的思路,有助于理解代碼,便于體會

這里將講解一些題目中的難點(diǎn),以及實(shí)現(xiàn)該操作的函數(shù)的一些細(xì)節(jié)(建議結(jié)合代碼一起看
createList:將數(shù)據(jù)放入鏈表,基本沒什么坑,略

printResult:把隊(duì)列內(nèi)容輸出,基本也沒什么坑,唯一要注意的是最后一個節(jié)點(diǎn)的下一個地址是-1,不能以%05的方式輸出,略

putListToQueue:作用是排序鏈表并將之傳入隊(duì)列K
我們傳入初始地址(是頭節(jié)點(diǎn)中包含的信息,第一個節(jié)點(diǎn)的地址),每次通過地址找到對應(yīng)的節(jié)點(diǎn),然后更新一下地址為該節(jié)點(diǎn)的next,再通過address尋找下一個節(jié)點(diǎn),直到排序完整個鏈表。

這里需要說明一下地址:我們通過地址尋找節(jié)點(diǎn),不是使用結(jié)構(gòu)體的計(jì)算機(jī)地址,是格式中的address!,結(jié)構(gòu)體的的計(jì)算機(jī)地址只是我們訪問各個節(jié)點(diǎn)的橋梁

這里分享一下寫這道題的有關(guān)這個函數(shù)的思路:
在代碼中我使用了head->nextNode != 0作為for循環(huán)的中止條件,后面補(bǔ)了address != -1的終止條件。
之所以補(bǔ)了這個條件,便是因?yàn)?strong>測試點(diǎn)6的原因。測試點(diǎn)6導(dǎo)致我被卡了好久啊啊?。。。?/p>

測試點(diǎn)6的意思便是,給出的數(shù)據(jù)有些Node孤立的!也就是說,最后輸出的結(jié)點(diǎn)數(shù)和給的結(jié)點(diǎn)數(shù)是不一樣的?。?/strong>這就導(dǎo)致了K有可能是大于N的?。?!

這個測試點(diǎn)坑了我好久orz,我之前以為給出的數(shù)據(jù)會排出一個和原來結(jié)點(diǎn)數(shù)一樣的鏈表(所以head->nextNode != 0作為for循環(huán)的中止條件)

因?yàn)闇y試點(diǎn)6 補(bǔ)了一個address != -1的終止條件,因?yàn)楫?dāng)address被更新到了-1表示已經(jīng)排序完成。雖然本來就可以用這個條件作為終止條件,但是沒有想到會出現(xiàn)孤立點(diǎn),便沒有使用

Notice:這里只是按照地址,排列好了鏈表

reserveQueue:將數(shù)據(jù)反轉(zhuǎn),并且連接地址
反轉(zhuǎn):每次rear前進(jìn)K個元素,然后用一個變量等于rear-1,進(jìn)行的pop來反轉(zhuǎn),length表示剩余結(jié)點(diǎn)數(shù),如果小于K了,便直接順序輸出便可
Notice:
1.這里反轉(zhuǎn)好了鏈表但是地址可能打亂了
2.反轉(zhuǎn)好的元素在隊(duì)列里

連接地址:如果K == 1,無需連接直接return,因?yàn)榈刂繁旧砭褪桥藕玫?/p>

當(dāng)K != 1直接把下一個元素的地址賦給上一個元素的next即可
不過這一塊我寫復(fù)雜了,是因?yàn)楫?dāng)時(shí)沒考慮到M%K = 0的情況,直接以next->node[temp]->next != -1作為終止條件,是因?yàn)楫?dāng)時(shí)偷懶不想直接使用** i < lengths**作為遍歷條件,而直接以找到-1作為終止條件,當(dāng)M%K = 0會造成一部分元素沒有連接。

i < lengths這個條件兩個情況都適配,只連接M-1個節(jié)點(diǎn),最后一個節(jié)點(diǎn)的next直接寫-1,但是我懶得改了,希望沒有讓你們感到誤解,困惑

image.png

Code

#include<stdio.h>
#include<malloc.h>
#define size 100000
typedef struct node {
    int address;
    int data;
    int next;
    struct node* nextNode;
}Node, *Pnode;
typedef struct queue {
    Pnode node[size+1];
    int front;
    int rear;
}Queue,*Pqueue;
Pnode createList(int lenght);
void putListToQueue(Pnode head, Pqueue queue, int address);
void reserveQueue(Pqueue first, Pqueue next, int reserveNumber, int length);
void printResult(Pqueue queue);
int main(void) {
    Node iniInformation;
    int lenght;//數(shù)列長度
    int reserveNumber;//反轉(zhuǎn)間隔
    int nextAddress;//address of next node
    Pnode head = 0;
    Pqueue queueResult = (Pqueue)malloc(sizeof(Queue));
    Pqueue temp = (Pqueue)malloc(sizeof(Queue));
    scanf("%d %d %d", &iniInformation.address, &iniInformation.data, &iniInformation.next);
    lenght = iniInformation.data;
    reserveNumber = iniInformation.next;
    nextAddress = iniInformation.address;
    //初始化隊(duì)列的front and rear
    temp->front = temp->rear = 0;
    queueResult->front = queueResult->rear = 0;
    //create list
    head = createList(lenght);
    //排序數(shù)列
    putListToQueue(head, temp,nextAddress);
    //反轉(zhuǎn)數(shù)列
    reserveQueue(temp, queueResult, reserveNumber, temp->front - temp->rear);
    //output result
    printResult(queueResult);
    return 0;
}
Pnode createList(int lenght) {
    Pnode temp = 0;
    Pnode head = temp = (Pnode)malloc(sizeof(Node));

    for (int i = 0; i < lenght; i++) {
        Pnode node = (Pnode)malloc(sizeof(Node));
        scanf("%d %d %d", &node->address, &node->data, &node->next);
        temp->nextNode = node;
        temp = node;
        temp->nextNode = 0;
    }
    return head;
}
void printResult(Pqueue queue) {
    Pnode node = 0;
    for (;queue->rear != queue->front;) {
        node = queue->node[queue->rear];
        if (node->next != -1) {
            printf("%0.5d %d %0.5d\n", node->address, node->data, node->next);
        }else{
            printf("%0.5d %d %d", node->address, node->data, node->next);
        }
        queue->rear++;
    }
}
void putListToQueue(Pnode head, Pqueue queue, int address) {
    Pnode temp = 0; 
    for (; head->nextNode != 0;) {
        temp = head;
        for (;temp->nextNode != 0 && temp->nextNode->address != address;) {
            temp = temp->nextNode;
        }
        address = temp->nextNode->next;
        queue->node[queue->front++] = temp->nextNode;
        temp->nextNode = temp->nextNode->nextNode;
        if (address == -1) {
            break;
        }
    }
}
void reserveQueue(Pqueue first, Pqueue next, int reserveNumber, int length) {
    int temp;
    int lengths = length;
    for (; length > 0;) {
        if (length >= reserveNumber) {
            first->rear += reserveNumber;
            temp = first->rear  - 1;
            for (int i = 0; i < reserveNumber; i++) {
                next->node[next->front++] = first->node[temp--];
            }
        }else{
            for (;first->rear != first->front;) {
                next->node[next->front++] = first->node[first->rear++];
            }
        }
        length -= reserveNumber;
    }
    if (reserveNumber == 1) {
        return;
    }
    temp = next->rear;
    if (lengths % reserveNumber == 0)
    {
        for (int i = 1; i < lengths; i++, temp++)
        {
            next->node[temp]->next = next->node[temp + 1]->address;
        }
        next->node[temp]->next = -1;
    }else{
        for (; next->node[temp]->next != -1; temp++) {
            next->node[temp]->next = next->node[temp + 1]->address;
        }
    }
}

Summary

這道題還是很有難度的,尤其是測試點(diǎn)6!?。懥诉@題總體給我的感覺就是我還需要加強(qiáng)考慮各種漏洞(特殊情況)
像這次便導(dǎo)致我會有很多地方會有打補(bǔ)丁式的代碼,便如我前面的講解思路一樣,putListToQueue() and reserveQueue()加了注水代碼,只需要一個條件便可以適配,卻多加了一個 if-else

雖然這個看起來挺復(fù)雜的(不過我感覺用鏈表更麻煩,反正我是不想怎么實(shí)現(xiàn)了!還有個使用三個數(shù)組來實(shí)現(xiàn)的,僅僅用了三十多行代碼,有興趣可以看一看https://blog.csdn.net/liyuanyue2017/article/details/83269991

不過利用了隊(duì)列,棧,鏈表來實(shí)現(xiàn)做法,使得算法快了許多,因?yàn)樵诳磩e人的測試時(shí)間大多數(shù)都是100-200ms,這個基本在50-60ms

不過寫完了這個博客突然又覺得簡單好多(才怪),可能是寫了博客自己總結(jié)一番的原因hhh

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容