当然这里只是展示复杂度的计算过程,这个算法明显是可以把func2(x, n / 2)提取出来只算一次的:
1 2 3 4 5 6 7 8 9
intfunc3(int x, int n){ if (n == 0) return1; if (n == 1) return x; int t = func3(x, n / 2); // 提取出来,只需要算一次 if (n % 2 == 1) { return t * t * x; } return t * t; }
此时的时间复杂度是多少呢?
首先看出现了几个自调用函数,显然是1个:func3(x, n / 2)(调用了两次),因此递归树是一叉树;
intfunc3_bit(int x, int n){ int result = 1; while (n > 0) { if (n & 1) { // 代替 n % 2 == 1 result *= x; } x *= x; n >>= 1; // 代替 n /= 2 } return result; }
该算法的时间复杂度仍然为 O(log2n)(因为迭代次数为 log2n)。
阶乘计算
1 2 3 4
intfactorial(int n){ if (n <= 1) return1; return n * factorial(n - 1); }
出现了1次自调用,树结构为一叉树;
递归深度为 n;
T(n)=di∈D∑j∈di∑O(1)=i=0∑nO(1)≃O(n)
因此时间复杂度为:O(n)。
二分查找
1 2 3 4 5 6 7 8
intbinarySearch(int arr[], int l, int r, int x){ if (l > r) return-1; int mid = l + (r - l) / 2; if (arr[mid] == x) return mid; if (arr[mid] > x) returnbinarySearch(arr, l, mid - 1, x); returnbinarySearch(arr, mid + 1, r, x); }
voidquickSort(int arr[], int low, int high){ if (low < high) { int pi = partition(arr, low, high); // O(n) quickSort(arr, low, pi - 1); // T(k) quickSort(arr, pi + 1, high); // T(n-k-1) } }
voidhanoi(int n, char from, char to, char aux){ if (n == 1) { cout << "Move disk 1 from " << from << " to " << to << endl; return; } hanoi(n - 1, from, aux, to); // T(n-1) cout << "Move disk " << n << " from " << from << " to " << to << endl; hanoi(n - 1, aux, to, from); // T(n-1) }
自调用次数为2,递归树结构为二叉树;
最大深度为n;
计算树的所有节点的时间复杂度之和,时间复杂度为O(2n)。
习题
练习建议
画出递归树:对于每个算法,尝试画出递归调用的树状结构
计算递归深度:确定递归的最大深度
分析每层工作量:计算递归树每层的时间复杂度
数组求和
1 2 3 4
intarraySum(int arr[], int n){ if (n <= 0) return0; return arr[n - 1] + arraySum(arr, n - 1); }
时间复杂度分析:
单次递归调用
递归深度:n
时间复杂度:O(n)
二叉树高度计算
1 2 3 4 5 6
inttreeHeight(TreeNode* root){ if (root == nullptr) return0; int leftHeight = treeHeight(root->left); int rightHeight = treeHeight(root->right); returnmax(leftHeight, rightHeight) + 1; }
时间复杂度分析:
每个节点访问一次
时间复杂度:O(n) (n 为节点数)
全排列生成
1 2 3 4 5 6 7 8 9 10 11
voidpermute(vector<int>& nums, int start, vector<vector<int>>& result){ if (start == nums.size()) { result.push_back(nums); return; } for (int i = start; i < nums.size(); i++) { swap(nums[start], nums[i]); permute(nums, start + 1, result); swap(nums[start], nums[i]); // 回溯 } }
时间复杂度分析:
递归深度:n
每层分支数递减:n,n−1,n−2,...,1
时间复杂度:O(n!)
子集生成
1 2 3 4 5 6 7 8 9 10 11 12
voidsubsets(vector<int>& nums, int index, vector<int>& current, vector<vector<int>>& result){ if (index == nums.size()) { result.push_back(current); return; } // 不包含当前元素 subsets(nums, index + 1, current, result); // 包含当前元素 current.push_back(nums[index]); subsets(nums, index + 1, current, result); current.pop_back(); // 回溯 }
时间复杂度分析:
递归树为完全二叉树
递归深度:n
总节点数:2n
时间复杂度:O(2n)
快速幂计算
1 2 3 4 5 6 7 8 9 10 11
doublepower(double x, int n){ if (n == 0) return1; if (n == 1) return x; double half = power(x, n / 2); if (n % 2 == 0) { return half * half; } else { return half * half * x; } }
时间复杂度分析:
每次递归问题规模减半
递归深度:log2n
时间复杂度:O(log2n)
最大公约数(欧几里得算法)
1 2 3 4
intgcd(int a, int b){ if (b == 0) return a; returngcd(b, a % b); }