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 -> 5
struct 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 5
struct 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;
}
