C++递归算法
簡介
遞歸(英語:Recursion),在數學和計算機科學中是指在函數的定義中使用函數自身的方法,在計算機科學中還額外指一種通過重復將問題分解為同類的子問題而解決問題的方法。
遞歸
遞歸的基本思想是某個函數直接或者間接地調用自身,這樣原問題的求解就轉換為了許多性質相同但是規模更小的子問題。求解時只需要關注如何把原問題劃分成符合條件的子問題,而不需要過分關注這個子問題是如何被解決的。
遞歸在數學中非常常見。例如,集合論對自然數的正式定義是:1 是一個自然數,每個自然數都有一個后繼,這一個后繼也是自然數。
遞歸代碼最重要的兩個特征:結束條件和自我調用。自我調用是在解決子問題,而結束條件定義了最簡子問題的答案。
int func(傳入數值) {if (終止條件) return 最小子問題解;return func(縮小規模); }為什么要寫遞歸?
1.結構清晰,可讀性強。例如,分別用不同的方法實現歸并排序:
// 不使用遞歸的歸并排序算法 template <typename T> void merge_sort(vector<T> a) {int n = a.size();for (int seg = 1; seg < n; seg = seg + seg)for (int start = 0; start < n - seg; start += seg + seg)merge(a, start, start + seg - 1, std::min(start + seg + seg - 1, n - 1)); }// 使用遞歸的歸并排序算法 template <typename T> void merge_sort(vector<T> a, int front, int end) {if (front >= end) return;int mid = front + (end - front) / 2;merge_sort(a, front, mid);merge_sort(a, mid + 1, end);merge(a, front, mid, end); }顯然,遞歸版本比非遞歸版本更易理解。遞歸版本的做法一目了然:把左半邊排序,把右半邊排序,最后合并兩邊。而非遞歸版本看起來不知所云,充斥著各種難以理解的邊界計算細節,特別容易出 bug,且難以調試。
練習分析問題的結構。當發現問題可以被分解成相同結構的小問題時,遞歸寫多了就能敏銳發現這個特點,進而高效解決問題。
遞歸的缺點
在程序執行中,遞歸是利用堆棧來實現的。每當進入一個函數調用,棧就會增加一層棧幀,每次函數返回,棧就會減少一層棧幀。而棧不是無限大的,當遞歸層數過多時,就會造成?棧溢出?的后果。
顯然有時候遞歸處理是高效的,比如歸并排序;有時候是低效的,比如數孫悟空身上的毛,因為堆棧會消耗額外空間,而簡單的遞推不會消耗空間。比如這個例子,給一個鏈表頭,計算它的長度:
// 典型的遞推遍歷框架 int size(Node *head) {int size = 0;for (Node *p = head; p != nullptr; p = p->next) size++;return size; }// 我就是要寫遞歸,遞歸天下第一 int size_recursion(Node *head) {if (head == nullptr) return 0;return size_recursion(head->next) + 1; }總結
- 上一篇: 详解 Visual C# 数据库编程
- 下一篇: 作业自动提示功能设计思路