你将如何迭代这个二维链表?

Posted

技术标签:

【中文标题】你将如何迭代这个二维链表?【英文标题】:How would you iterate this 2D linked list? 【发布时间】:2018-04-16 23:14:21 【问题描述】:

typedef struct _NODE 

    char          *pszName; 
    unsigned long ulIntVal;  
    char          *pszString; 
    struct _NODE  *pNext;  
    struct _NODE  *pDown; 
 NODE;

我可以做这样的事情..

NODE *pHEad;

while (pHead != NULL) 
    printf("%s", pHead->pDown->pszName);
    pHead = pHead->pNext;

.. 但它只会给我每个下一个节点下的一个节点。如果它又是该节点下的另一个节点怎么办?又在那个之下?或者如果pNext 附加到pDown

【问题讨论】:

递归(名词)见递归。 当然,会调查的,谢谢。 你不能这样做吗:pHead -> pNext -> pNext -> pszName。您可以使用循环或像这样的简单语句尽可能深入。这是你问的吗? 是的,这就是我要问的,但这样做会很困难,因为我不知道相互连接的节点数量。我将尝试为此目的创建一个递归函数。 【参考方案1】:

在最简单的情况下,您可以使用如下递归函数:

void processNode(NODE *current) 
    if (current != NULL) 
        printf("%s", current->pszName);

        processNode(current->pNext);
        processNode(current->pDown);
    


int main(void) 
    NODE *pHead;
    /* ... Do something to fill your list ... */
    processNode(pHead);
    /* ... */

另外请注意,这可能会导致函数调用的深度嵌套,具体取决于您处理的列表。因此,如果您使用的是堆栈大小有限的嵌入式系统,或者您正在处理巨大的列表,您可能会用完堆栈。在这种情况下,您应该找到另一种处理方法。

请注意,这将首先处理 pNext-list,然后开始处理最后一个节点的 pDown-list 的第一个节点。所以假设如下结构(右边是pNext,往下是pDown):

pHead -> p1 -------> p2
         |- p1_1     |- p2_1 -> p2_1_1
         \- p1_2     |- p2_2
                     \- p2_3 -> p2_3_1

它应该按以下顺序打印节点:

pHead, p1, p2, p2_1, p2_1_1, p2_2, p2_3, p2_3_1, p1_1, p1_2

【讨论】:

【参考方案2】:

看看这个答案。不要被代码的数量所淹没。我已经添加了足够的 cmets 来帮助您继续。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Node
    char data[100]; // Assume that this linked list will contain only 100 chars of data
    struct Node* next;
 NODE;

// Global Variables are bad, but oh well.
NODE* head = NULL;

// Function to create a node
NODE* createNode(char* str)

    // First allocate memory for struct
    NODE* newNode = malloc(sizeof(NODE));
    if(newNode == NULL)
    
        printf("Unable to create a new node.");
    
    else
    

        // Use strcpy or strncpy or memcpy instead of doing something like newNode -> data = str, which changes the pointer, but doesn't copy the contents
        // That is do not do newNode -> data = "hello" or something
        strncpy(newNode -> data, str, strlen(str));
        newNode -> next = NULL;
    
    return newNode;


void addNode(char* str)

    // Returns a node which contains str, but points to NULL
    NODE* newNode = createNode(str);

    // If the linked list is empty, then we make this node itself as the first node(or head)
    if(head == NULL)
    
        head = newNode;
    
    // Else if the linked list is not empty, then we add this node at the start of the linked list
    else
    
        newNode -> next = head;
        head = newNode;
    


int main()

   // Example Linked List Generated(say you already have it in some form)
   addNode("This");
   addNode("Is");
   addNode("Linked List");

   // Now let's print the linked list
   // Temporary NODE pointer ptr is used in order to not mess with the original NODE pointer head.
    NODE* ptr = head; 

    // Traverse through the linked list starting from head and at the same time printing the corresponding data, until ptr is null
    // This ptr != NULL check is exactly what you are looking for. This is your way of stopping the traversal of Linked List once you
    // are at the end of it. You don't have to know the number of nodes to stop the traversal this way.
    while(ptr != NULL)
    
        printf("%s ", ptr -> data);
        ptr = ptr -> next;
    

但是请注意,输出将以相反的顺序打印,因为在这个链表的实现中,我们在后面添加东西。只需尝试运行程序并从main 函数开始读取程序。我已将代码分成单独的函数,以便您更容易理解。只需先运行代码即可了解正在发生的事情。

【讨论】:

【参考方案3】:

如果您想避免堆栈溢出的可能性,您也可以通过添加队列来使用迭代而不是递归 - 尽管这会使用更多的堆内存,并且仍然存在耗尽的风险如果您有一个大列表或者如果您在内存受限的系统上运行,则使用堆内存。重要的部分是末尾的print_list 函数;其他东西只是我提供的(大部分)自我管理队列实现:

typedef struct node_queue NodeQueue;
struct node_queue 
    NODE *n;
    NodeQueue *next;
;

/*
 * Add an item to the end of the queue.
 *
 * If the item could not be added, 0 is returned.
 * Otherwise, a nonzero value is returned.
 */
int enqueue(NodeQueue **headp, NodeQueue **endp, NODE *n)

    NodeQueue *old_end = *endp;
    NodeQueue *new_end;

    new_end = malloc(sizeof *new_end);
    if (new_end == NULL) 
        return 0;
    
    new_end->n = n;
    new_end->next = NULL;

    if (old_end != NULL) 
        old_end->next = new_end;
    
    if (*headp == NULL) 
        *headp = new_end;
    
    *endp = new_end;
    return 1;


/*
 * Remove an item from the head of the queue,
 * storing it in the object that "nret" points to.
 *
 * If no item is in the queue, 0 is returned.
 * Otherwise, a nonzero value is returned.
 */
int dequeue(NodeQueue **headp, NodeQueue **endp, NODE **nret)

    NodeQueue *old_head = *headp;
    NodeQueue *new_head;
    if (old_head == NULL) 
        return 0;
    
    if (nret != NULL) 
        *nret = old_head->n;
    
    new_head = old_head->next;
    free(old_head);
    if (new_head == NULL) 
        *endp = NULL;
    
    *headp = new_head;
    return 1;


void print_list(NODE *start)

    NodeQueue *head = NULL;
    NodeQueue *end = NULL;
    NODE *current;

    current = start;

    /* Iterate all `pNext` nodes, then pop each `pDown` node and repeat. */
    for (;;) 
        /* Add the "down" node to the node queue. */
        if (current->pDown != NULL) 
            if (!enqueue(&head, &end, current->pDown)) 
                perror("warning: could not add node to queue");
            
        

        printf("%s", current->pszNode);

        /*
         * Move to the "next" node.
         * If there is no next node, get the first "down" node from the queue.
         * If there is no "down" node, break the loop to end processing.
         */
        current = current->pNext;
        if (current == NULL) 
            if (!dequeue(&head, &end, &current)) 
                break;
            
        
    

这将遍历所有 pNext 项目,然后再移动到 pDown 项目。以下二维列表将打印为A B C D E F G H I J K L M N O P Q

A
|
B--C
|
D--E-----------F
   |           |
   G-----H     I-----J
   |     |     |     |
   K--L  M--N  O     P
         |
         Q

您可以在print_list 函数中通过交换pNextpDown 来反转pDown/pNext 的优先级,因此pNext 项被添加到队列中,pDown 项被添加到队列中迭代直到用尽,这会将项目的打印顺序更改为A B D C E G K F I O H M Q L J P N,除非您更改列表的结构。

您可以在https://repl.it/NjyV/1 看到同时使用上面的代码和上面的第一个示例二维链表的示例,尽管我更改了NODE 的定义以使使用其字段的代码更简单一些。

【讨论】:

以上是关于你将如何迭代这个二维链表?的主要内容,如果未能解决你的问题,请参考以下文章

如何迭代或递归确定二维数组中的邻居?

如何在 Lua 中的二维表上创建迭代器?

如何循环遍历二维数组以在页面上显示数据?

对java中hashmap深入理解

迭代二维python列表[重复]

为啥迭代二维数组时循环的顺序会影响性能?