C语言递归代码转迭代实现:如何将XML树元素名称打印函数改为迭代版本?
Great question! Recursive traversals are clean and easy to read, but sometimes you need an iterative approach—maybe to avoid stack overflow with deeply nested XML, or for specific performance constraints. Let's walk through how to convert your recursive function to an iterative one, step by step.
First, Understand the Recursive Logic
Your original function does a depth-first pre-order traversal:
- Iterate through all sibling nodes starting from
a_node(usingcur_node->next). - For each element node, print its name.
- Recursively traverse all children of the current node before moving to the next sibling.
The key here is that we process a node, then dive into its entire subtree, then move to the next sibling. To replicate this iteratively, we'll use a stack to simulate the call stack of the recursive approach.
Iterative Implementation
We'll need a simple stack structure (since C doesn't have a built-in stack) to keep track of nodes we need to process. Here's the full implementation:
#include <stdio.h> #include <stdlib.h> #include <libxml/tree.h> // Simple stack implementation to hold xmlNode pointers typedef struct { xmlNode** data; int top; int capacity; } Stack; // Initialize a stack with an initial capacity Stack* stack_init(int initial_capacity) { Stack* stack = (Stack*)malloc(sizeof(Stack)); stack->capacity = initial_capacity; stack->data = (xmlNode**)malloc(stack->capacity * sizeof(xmlNode*)); stack->top = -1; return stack; } // Check if the stack is empty int stack_is_empty(Stack* stack) { return stack->top == -1; } // Resize the stack when it's full (double the capacity) void stack_resize(Stack* stack) { stack->capacity *= 2; stack->data = (xmlNode**)realloc(stack->data, stack->capacity * sizeof(xmlNode*)); } // Push a node onto the stack void stack_push(Stack* stack, xmlNode* node) { if (stack->top == stack->capacity - 1) { stack_resize(stack); } stack->data[++stack->top] = node; } // Pop a node from the stack (returns NULL if empty) xmlNode* stack_pop(Stack* stack) { if (stack_is_empty(stack)) { return NULL; } return stack->data[stack->top--]; } // Clean up stack memory void stack_destroy(Stack* stack) { free(stack->data); free(stack); } // Iterative version of print_element_names static void print_element_names_iterative(xmlNode* a_node) { if (!a_node) { return; } Stack* stack = stack_init(10); stack_push(stack, a_node); while (!stack_is_empty(stack)) { xmlNode* cur_node = stack_pop(stack); // Print the element name if it's an element node if (cur_node->type == XML_ELEMENT_NODE) { printf("node type: Element, name: %s\n", cur_node->name); } // Critical: Push next sibling first, then children // Since stack is LIFO, children will be processed before next sibling if (cur_node->next) { stack_push(stack, cur_node->next); } if (cur_node->children) { stack_push(stack, cur_node->children); } } // Don't forget to free the stack to avoid memory leaks stack_destroy(stack); }
Key Details Explained
- Stack Structure: We implement a dynamic stack that resizes when full to handle XML trees of any size.
- Traversal Order:
- We push the next sibling first, then the children of the current node. Since stacks are last-in-first-out (LIFO), this ensures we process the entire subtree of the current node before moving to the next sibling—matching the exact order of your recursive function.
- Memory Management: We explicitly initialize and destroy the stack to avoid memory leaks, which is important in C.
- Edge Cases: We handle
NULLinput nodes and empty stacks to prevent crashes.
Testing the Iterative Function
This function will produce exactly the same output as your recursive version. For example, if you have an XML tree like:
<root> <child1> <grandchild1/> </child1> <child2/> </root>
Both functions will print:
node type: Element, name: root node type: Element, name: child1 node type: Element, name: grandchild1 node type: Element, name: child2
内容的提问来源于stack exchange,提问作者Rocket

