C++递归算法
簡介
遞歸(英語:Recursion),在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中是指在函數(shù)的定義中使用函數(shù)自身的方法,在計(jì)算機(jī)科學(xué)中還額外指一種通過重復(fù)將問題分解為同類的子問題而解決問題的方法。
遞歸
遞歸的基本思想是某個(gè)函數(shù)直接或者間接地調(diào)用自身,這樣原問題的求解就轉(zhuǎn)換為了許多性質(zhì)相同但是規(guī)模更小的子問題。求解時(shí)只需要關(guān)注如何把原問題劃分成符合條件的子問題,而不需要過分關(guān)注這個(gè)子問題是如何被解決的。
遞歸在數(shù)學(xué)中非常常見。例如,集合論對自然數(shù)的正式定義是:1 是一個(gè)自然數(shù),每個(gè)自然數(shù)都有一個(gè)后繼,這一個(gè)后繼也是自然數(shù)。
遞歸代碼最重要的兩個(gè)特征:結(jié)束條件和自我調(diào)用。自我調(diào)用是在解決子問題,而結(jié)束條件定義了最簡子問題的答案。
int func(傳入數(shù)值) {if (終止條件) return 最小子問題解;return func(縮小規(guī)模); }為什么要寫遞歸?
1.結(jié)構(gòu)清晰,可讀性強(qiáng)。例如,分別用不同的方法實(shí)現(xiàn)歸并排序:
// 不使用遞歸的歸并排序算法 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); }顯然,遞歸版本比非遞歸版本更易理解。遞歸版本的做法一目了然:把左半邊排序,把右半邊排序,最后合并兩邊。而非遞歸版本看起來不知所云,充斥著各種難以理解的邊界計(jì)算細(xì)節(jié),特別容易出 bug,且難以調(diào)試。
練習(xí)分析問題的結(jié)構(gòu)。當(dāng)發(fā)現(xiàn)問題可以被分解成相同結(jié)構(gòu)的小問題時(shí),遞歸寫多了就能敏銳發(fā)現(xiàn)這個(gè)特點(diǎn),進(jìn)而高效解決問題。
遞歸的缺點(diǎn)
在程序執(zhí)行中,遞歸是利用堆棧來實(shí)現(xiàn)的。每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用,棧就會增加一層棧幀,每次函數(shù)返回,棧就會減少一層棧幀。而棧不是無限大的,當(dāng)遞歸層數(shù)過多時(shí),就會造成?棧溢出?的后果。
顯然有時(shí)候遞歸處理是高效的,比如歸并排序;有時(shí)候是低效的,比如數(shù)孫悟空身上的毛,因?yàn)槎褩念~外空間,而簡單的遞推不會消耗空間。比如這個(gè)例子,給一個(gè)鏈表頭,計(jì)算它的長度:
// 典型的遞推遍歷框架 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; }總結(jié)
- 上一篇: 详解 Visual C# 数据库编程
- 下一篇: effective c++条款11扩展—