C语言需要掌握的基础知识点之递归
C语言需要掌握的基础知识点之递归
递归是C语言中一种重要的编程技术,它允许函数调用自身来解决问题。递归可以将复杂问题分解为更小的相似问题,直到达到基本情况。
递归的基本概念
递归是一个函数直接或间接调用自身的过程。每个递归函数必须包含两个部分:
基本情况:递归终止的条件
递归情况:函数调用自身的部分
递归的基本结构
返回值类型 函数名(参数) {// 1. 基本情况(终止条件)if (满足终止条件) {return 基础解;}// 2. 递归情况return 函数名(修改后的参数);
}
经典的递归示例
阶乘计算
#include <stdio.h>// 递归计算阶乘
long factorial(int n) {// 基本情况if (n == 0 || n == 1) {return 1;}// 递归情况return n * factorial(n - 1);
}int main() {int numbers[] = {0, 1, 5, 10};int count = sizeof(numbers) / sizeof(numbers[0]);for (int i = 0; i < count; i++) {printf("%d! = %ld\n", numbers[i], factorial(numbers[i]));}return 0;
}
斐波那契数列
#include <stdio.h>// 递归计算斐波那契数列
long fibonacci(int n) {// 基本情况if (n == 0) return 0;if (n == 1) return 1;// 递归情况return fibonacci(n - 1) + fibonacci(n - 2);
}// 优化版本:使用记忆化递归
long fibonacciMemo(int n, long memo[]) {if (n == 0) return 0;if (n == 1) return 1;// 如果已经计算过,直接返回结果if (memo[n] != -1) {return memo[n];}// 计算并存储结果memo[n] = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo);return memo[n];
}int main() {int n = 10;printf("斐波那契数列前%d项:\n", n);for (int i = 0; i < n; i++) {printf("F(%d) = %ld\n", i, fibonacci(i));}// 使用记忆化递归printf("\n使用记忆化递归:\n");long memo[100];for (int i = 0; i < 100; i++) memo[i] = -1;for (int i = 0; i < n; i++) {printf("F(%d) = %ld\n", i, fibonacciMemo(i, memo));}return 0;
}
汉诺塔问题
#include <stdio.h>// 递归解决汉诺塔问题
void hanoi(int n, char from, char to, char aux) {// 基本情况:只有一个盘子if (n == 1) {printf("将盘子 1 从 %c 移动到 %c\n", from, to);return;}// 递归步骤:// 1. 将n-1个盘子从源柱移动到辅助柱hanoi(n - 1, from, aux, to);// 2. 将第n个盘子从源柱移动到目标柱printf("将盘子 %d 从 %c 移动到 %c\n", n, from, to);// 3. 将n-1个盘子从辅助柱移动到目标柱hanoi(n - 1, aux, to, from);
}int main() {int n = 3;printf("汉诺塔问题,%d个盘子的移动步骤:\n", n);hanoi(n, 'A', 'C', 'B');return 0;
}
递归在数据结构中的应用
链表递归操作
#include <stdio.h>
#include <stdlib.h>// 链表节点定义
struct Node {int data;struct Node* next;
};// 递归遍历链表
void recursiveTraverse(struct Node* node) {// 基本情况:空节点if (node == NULL) {return;}// 先处理当前节点printf("%d ", node->data);// 递归处理下一个节点recursiveTraverse(node->next);
}// 递归反转链表
struct Node* recursiveReverse(struct Node* node) {// 基本情况:空节点或只有一个节点if (node == NULL || node->next == NULL) {return node;}// 递归反转剩余部分struct Node* newHead = recursiveReverse(node->next);// 将当前节点连接到反转后链表的末尾node->next->next = node;node->next = NULL;return newHead;
}// 递归计算链表长度
int recursiveLength(struct Node* node) {if (node == NULL) {return 0;}return 1 + recursiveLength(node->next);
}// 创建链表节点
struct Node* createNode(int data) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));newNode->data = data;newNode->next = NULL;return newNode;
}int main() {// 创建链表: 1 -> 2 -> 3 -> 4 -> 5struct Node* head = createNode(1);head->next = createNode(2);head->next->next = createNode(3);head->next->next->next = createNode(4);head->next->next->next->next = createNode(5);printf("原链表: ");recursiveTraverse(head);printf("\n");printf("链表长度: %d\n", recursiveLength(head));head = recursiveReverse(head);printf("反转后链表: ");recursiveTraverse(head);printf("\n");return 0;
}
二叉树递归操作
#include <stdio.h>
#include <stdlib.h>// 二叉树节点定义
struct TreeNode {int data;struct TreeNode* left;struct TreeNode* right;
};// 递归创建二叉树节点
struct TreeNode* createNode(int data) {struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));newNode->data = data;newNode->left = NULL;newNode->right = NULL;return newNode;
}// 递归前序遍历
void preorderTraversal(struct TreeNode* root) {if (root == NULL) {return;}printf("%d ", root->data); // 访问根节点preorderTraversal(root->left); // 遍历左子树preorderTraversal(root->right); // 遍历右子树
}// 递归中序遍历
void inorderTraversal(struct TreeNode* root) {if (root == NULL) {return;}inorderTraversal(root->left); // 遍历左子树printf("%d ", root->data); // 访问根节点inorderTraversal(root->right); // 遍历右子树
}// 递归后序遍历
void postorderTraversal(struct TreeNode* root) {if (root == NULL) {return;}postorderTraversal(root->left); // 遍历左子树postorderTraversal(root->right); // 遍历右子树printf("%d ", root->data); // 访问根节点
}// 递归计算树的高度
int treeHeight(struct TreeNode* root) {if (root == NULL) {return 0;}int leftHeight = treeHeight(root->left);int rightHeight = treeHeight(root->right);return 1 + (leftHeight > rightHeight ? leftHeight : rightHeight);
}// 递归查找节点
struct TreeNode* searchNode(struct TreeNode* root, int key) {if (root == NULL || root->data == key) {return root;}struct TreeNode* leftResult = searchNode(root->left, key);if (leftResult != NULL) {return leftResult;}return searchNode(root->right, key);
}int main() {// 创建二叉树// 1// / \// 2 3// / \// 4 5struct TreeNode* root = createNode(1);root->left = createNode(2);root->right = createNode(3);root->left->left = createNode(4);root->left->right = createNode(5);printf("前序遍历: ");preorderTraversal(root);printf("\n");printf("中序遍历: ");inorderTraversal(root);printf("\n");printf("后序遍历: ");postorderTraversal(root);printf("\n");printf("树的高度: %d\n", treeHeight(root));int searchKey = 4;struct TreeNode* found = searchNode(root, searchKey);if (found != NULL) {printf("找到节点 %d\n", searchKey);} else {printf("未找到节点 %d\n", searchKey);}return 0;
}
递归的数学应用
最大公约数(GCD)
#include <stdio.h>// 递归计算最大公约数(欧几里得算法)
int gcd(int a, int b) {// 基本情况if (b == 0) {return a;}// 递归情况return gcd(b, a % b);
}int main() {int pairs[][2] = {{48, 18}, {56, 42}, {101, 103}};int count = sizeof(pairs) / sizeof(pairs[0]);for (int i = 0; i < count; i++) {int a = pairs[i][0];int b = pairs[i][1];printf("gcd(%d, %d) = %d\n", a, b, gcd(a, b));}return 0;
}
幂运算
#include <stdio.h>// 递归计算幂
double power(double base, int exponent) {// 基本情况if (exponent == 0) {return 1;}if (exponent == 1) {return base;}// 处理负指数if (exponent < 0) {return 1 / power(base, -exponent);}// 递归情况:分治策略if (exponent % 2 == 0) {double half = power(base, exponent / 2);return half * half;} else {return base * power(base, exponent - 1);}
}int main() {printf("2^10 = %.0f\n", power(2, 10));printf("3^4 = %.0f\n", power(3, 4));printf("5^-2 = %.4f\n", power(5, -2));printf("2.5^3 = %.4f\n", power(2.5, 3));return 0;
}
递归的字符串操作
字符串反转
#include <stdio.h>
#include <string.h>// 递归反转字符串
void reverseString(char str[], int start, int end) {// 基本情况if (start >= end) {return;}// 交换首尾字符char temp = str[start];str[start] = str[end];str[end] = temp;// 递归处理子字符串reverseString(str, start + 1, end - 1);
}// 递归判断回文串
int isPalindrome(char str[], int start, int end) {// 基本情况if (start >= end) {return 1;}// 如果首尾字符不相等,不是回文if (str[start] != str[end]) {return 0;}// 递归检查子字符串return isPalindrome(str, start + 1, end - 1);
}int main() {char str1[] = "hello";char str2[] = "racecar";printf("原字符串: %s\n", str1);reverseString(str1, 0, strlen(str1) - 1);printf("反转后: %s\n", str1);printf("\n字符串 '%s' 是回文吗? %s\n", str2, isPalindrome(str2, 0, strlen(str2) - 1) ? "是" : "否");return 0;
}
递归的注意事项和优化
尾递归优化
#include <stdio.h>// 普通递归阶乘
long factorial(int n) {if (n == 0 || n == 1) {return 1;}return n * factorial(n - 1); // 不是尾递归
}// 尾递归阶乘
long factorialTail(int n, long accumulator) {if (n == 0 || n == 1) {return accumulator;}return factorialTail(n - 1, n * accumulator); // 尾递归
}// 包装函数
long factorialOptimized(int n) {return factorialTail(n, 1);
}int main() {int n = 5;printf("%d! = %ld (普通递归)\n", n, factorial(n));printf("%d! = %ld (尾递归)\n", n, factorialOptimized(n));return 0;
}
递归深度和栈溢出
#include <stdio.h>// 演示栈溢出的递归
void infiniteRecursion(int n) {printf("递归深度: %d\n", n);infiniteRecursion(n + 1); // 无限递归,会导致栈溢出
}// 安全的递归,有终止条件
void safeRecursion(int n, int maxDepth) {if (n > maxDepth) {printf("达到最大深度 %d\n", maxDepth);return;}printf("当前深度: %d\n", n);safeRecursion(n + 1, maxDepth);
}int main() {// 不要调用 infiniteRecursion(1),会导致栈溢出printf("安全递归演示:\n");safeRecursion(1, 10);return 0;
}
递归与迭代的比较
#include <stdio.h>
#include <time.h>// 递归斐波那契
long fibRecursive(int n) {if (n <= 1) return n;return fibRecursive(n - 1) + fibRecursive(n - 2);
}// 迭代斐波那契
long fibIterative(int n) {if (n <= 1) return n;long a = 0, b = 1, c;for (int i = 2; i <= n; i++) {c = a + b;a = b;b = c;}return b;
}int main() {int n = 40;clock_t start, end;printf("计算斐波那契数列第%d项:\n", n);// 测试迭代版本start = clock();long result1 = fibIterative(n);end = clock();printf("迭代结果: %ld, 时间: %f秒\n", result1, (double)(end - start) / CLOCKS_PER_SEC);// 测试递归版本start = clock();long result2 = fibRecursive(n);end = clock();printf("递归结果: %ld, 时间: %f秒\n", result2, (double)(end - start) / CLOCKS_PER_SEC);return 0;
}