有效避免使用递归

本文探讨了一种自动化脚本管理工具中数据结构的高效构建方法。通过改变递归方式,采用从底层向上的构建策略,大幅减少了构建所需时间,从数小时缩短至几秒。文章对比了常规递归与改进方法的执行效率。

实例:

 

自动化脚本管理工具,数据存储在服务器,读到客户端后,需要建立一个数据结构,从上到下顺序为:

Folder-->Object-->Control-->Operation-->parameter

Folder-->Object-->Operation-->Parameter

Folder-->Case-->CaseProcess-->Parameter

 

通过每个对象的ParentID和ID来建立数据结构

 

数据量:Folder:350, Object:400,Control:1000,Operation:3000,Parameter:2500

 

常规递归:

 

需要执行的次数为350X400X1000X3000X2500,要完成这样一个数据结构,需要超过20分钟的时间

 

没有一个用户愿意等待这么长时间的,那么有没有办法让时间短一些呢?那还是有的

 

方法:

从底层做起,有底向上,将350X400X1000X3000X2500的乘变成+,就能有效节省时间了

循环次数3000X2500

循环次数1000X3000

 

递归:350X400X1000X3000X2500  = 1050000000000000

不递归:1000X3000+3000X2500 + 400X1000+ 350X400 = 11040000

 

两相比较,谁快水漫一目了然了

 

 

我们要解决的问题是: 给定一个最多 31 位的数字字符串,通过重新排列得到**最小的合法偶数**(无前导 0,且为偶数)。若无法构造,则返回 `-1`。 现在要求:**使用递归方式实现核心逻辑**,并且仍然只能使用 `<iostream>` 和 `<string>` 头文件。 --- ### ✅ 解题思路(递归版): 我们将采用 **递归生成所有可能的排列组合** 的思想,但因为字符串长度最多为 31,全排列不可行(31! 太大),所以我们不能暴力递归所有排列。 但是注意:我们只需要**最小的偶数**,不需要所有排列。因此可以优化: > ❌ 不能用纯暴力递归生成所有排列(时间爆炸) > ✅ 但我们可以在 **排序后贪心地尝试构造候选解**,并用递归来辅助“选择下一个数字”的过程 —— 实际上更适合用回溯法。 然而题目要求“使用递归”,我们可以将关键子任务写成递归形式,例如: - 字符串排序(递归冒泡或归并) - 构造最小前缀(递归查找非零字符、拼接) - 或者使用递归回溯来生成候选排列(仅当长度较小时可行) 但由于长度最大为 31,**完全递归回溯所有排列不可行**。 --- ### 🔧 折中方案(合理使用递归 + 贪心剪枝): 我们结合以下方法: 1. 使用递归实现字符串排序(如递归版本的归并排序)。 2. 使用递归函数从剩余字符中构造最小有效前缀(避免前导0)。 3. 主体逻辑仍为枚举偶数结尾 + 贪心构造最小前缀,但在处理字符串操作时使用递归。 这样既满足“使用递归”的要求,又保证效率可行。 --- ### ✅ C++ 代码(仅用 `<iostream>`、`<string>`,含递归实现) ```cpp #include <iostream> #include <string> using namespace std; // 递归合并两个已排序的子串(辅助函数) void merge(string &s, int left, int mid, int right) { string temp = ""; int i = left, j = mid + 1; while (i <= mid && j <= right) { if (s[i] <= s[j]) { temp += s[i++]; } else { temp += s[j++]; } } while (i <= mid) temp += s[i++]; while (j <= right) temp += s[j++]; for (int k = 0; k < temp.length(); ++k) { s[left + k] = temp[k]; } } // 递归归并排序 void mergeSort(string &s, int left, int right) { if (left >= right) return; int mid = left + (right - left) / 2; mergeSort(s, left, mid); mergeSort(s, mid + 1, right); merge(s, left, mid, right); } // 递归查找第一个非零字符的位置 int findFirstNonZero(const string &s, int start, int n) { if (start >= n) return -1; if (s[start] != '0') return start; return findFirstNonZero(s, start + 1, n); } // 递归构造最小有效前缀(无前导0) string buildSmallestPrefix(const string &s) { int n = s.length(); if (n == 0) return ""; if (n == 1) return s; // 如果首字符不是 '0',直接返回 if (s[0] != '0') return s; // 否则找第一个非零字符 int firstNonZero = findFirstNonZero(s, 0, n); if (firstNonZero == -1) return "0"; // 全是0 string result = ""; result += s[firstNonZero]; // 把前面的所有0和其他字符加回来(除了 firstNonZero) for (int i = 0; i < firstNonZero; ++i) { result += s[i]; } for (int i = firstNonZero + 1; i < n; ++i) { result += s[i]; } return result; } // 主求解函数 string solve(string num) { int n = num.size(); mergeSort(num, 0, n - 1); // 递归排序 string best = ""; // 枚举每一位作为末尾偶数 for (int i = 0; i < n; ++i) { int d = num[i] - '0'; if (d % 2 != 0) continue; // 构造剩余字符串 string rem = ""; for (int j = 0; j < n; ++j) { if (i != j) rem += num[j]; } string prefix = ""; if (rem.empty()) { prefix = ""; // 单独处理 } else { mergeSort(rem, 0, rem.length() - 1); // 再排一次 prefix = buildSmallestPrefix(rem); // 递归构造最小前缀 } string candidate = prefix + num[i]; // 检查前导零:长度大于1时不能以0开头 if (candidate[0] == '0' && candidate.length() > 1) { continue; } // 更新最优解 if (best == "" || candidate < best) { best = candidate; } } return best == "" ? "-1" : best; } int main() { int T; cin >> T; while (T--) { string n; cin >> n; cout << solve(n) << endl; } return 0; } ``` --- ### 📌 解释: 1. **`mergeSort`**:递归实现归并排序,替代 `std::sort`。 2. **`findFirstNonZero`**:递归查找第一个非 `'0'` 字符。 3. **`buildSmallestPrefix`**:调用递归查找函数,构造最小有效前缀。 4. **主体逻辑不变**:枚举偶数结尾 → 剩余字符排序 → 构造最小前缀 → 拼接候选 → 取最小值。 5. 所有操作仅依赖 `<iostream>` 和 `<string>`。 --- ### ✅ 示例输入: ``` 3 312 135 204 ``` ### 输出: ``` 132 -1 204 ``` 正确无误。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值