成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C語言的“遞歸函數”這么難理解,為什么不丟棄它呢?

開發 后端
初學者在學習C語言的過程中,遇到“遞歸”的概念時,常常會感到迷惑。坦誠地說,“遞歸”在編程語言中的確是一個比較難理解的概念,而且“遞歸”能解決的問題,一般循環語句也能解決,從某種程度上來說,C語言中的“遞歸”和循環語句是等價的,既然如此,為什么C語言不“丟棄”難以理解的“遞歸”呢?

 初學者在學習C語言的過程中,遇到“遞歸”的概念時,常常會感到迷惑。坦誠地說,“遞歸”在編程語言中的確是一個比較難理解的概念,而且“遞歸”能解決的問題,一般循環語句也能解決,從某種程度上來說,C語言中的“遞歸”和循環語句是等價的,既然如此,為什么C語言不“丟棄”難以理解的“遞歸”呢?

[[315767]]

C語言為什么不丟棄“遞歸”?

其實,“遞歸”究竟是否難以理解取決于程序員看問題的角度。應該明白,C語言程序是為人們現實生活需求服務的,在我看來,如果脫離了實際問題,僅從編程語言的角度來看問題,“遞歸”的確比較難理解,相反,如果結合要實際解決的問題,有時“遞歸”反而更加清晰易懂。請看下面這段C語言代碼示例:

 

  1. int factorial(int n){    if(0 == n)        return 1;    else        return n*factorial(n-1);} 

factorial() 函數是一個典型的遞歸函數,雖然它的代碼很簡單,但如果僅從編程語言的角度來理解這個函數,的確有些難度——當 n!=0 時,函數似乎永遠在嵌套自己,雖說粗暴的逐步分析能夠得到函數的輸出,但是稍不留神就會出錯。

現在換個角度考慮 factorial() 函數,嘗試從該函數要解決的“實際需求”上分析,應該不難得到 factorial() 函數其實在說:

  • 如果 n 等于 0,那么 f(n) 等于 1,也即 f(0) 等于 1
  • 如果 n 不等于 0,那么 f(n) 等于 n*f(n-1)

將這兩句話轉換為數學語言,也就是:

 

C語言的“遞歸函數”這么難理解,為什么不丟棄它呢?

 

factorial()函數的數學描述

如果限定 n 為不小于 0 的整數,那么這顯然就是數學中整數階乘的定義,也即 factorial(n) 函數的輸出為 n!。可以看出,遞歸函數 factorial() 其實就是直白的使用C語言“描述”了 n! 的數學定義,因此從這個角度來看,“遞歸”似乎又不是那么難理解。

當然了,使用C語言的循環語句編寫程序計算 n! 也是可以的,讀者可自行編寫,應該能夠發現,循環語句計算 n! 更像是使用“笨方法”一點點的計算,它與“遞歸”實現從設計思維上來看是不同的。

因此,從上面這個例子可以看出,C語言中的“遞歸”倒不像是一種語法,而是一種“編程思維”,所以“丟棄”便無從說起了。當然了,嚴格來說,C語言對“遞歸”也是做了一定的支持,至少遞歸函數就屬于C語言的一種語法,這其實與C語言的基本設計思想有關:C語言從誕生至今,有一個特點是始終堅持的——盡可能的保持簡潔,給程序員比較大的自由。所以,既然“遞歸”思維是一個不錯的思維,C語言當然要提供遞歸函數予以支持了。

再來看一個例子

為了加深對遞歸的理解,這里再舉一個例子,請看下面這段C語言代碼:

  1. #include void test(int leftint right) { if (left >= rightreturnint mid = (left+right) /2; printf("before: %d %d %d\n"left, mid, right); test(left, mid); test(mid+1, right); printf("after : %d %d %d\n"left, mid, right);} int main() { test(0, 5); return 0;} 

 

C語言的“遞歸函數”這么難理解,為什么不丟棄它呢?

 

 

test() 函數的C語言代碼

test() 函數顯然是一個遞歸函數,它的代碼也比較簡單,只不過在其內部遞歸調用了兩次,稍稍復雜了一些。接下來,我們將分別從編程語言角度,和實際需求角度分別分析 test() 函數的功能。

首先,從編程語言角度來看,顯然 test() 函數會被多次調用,為了便于討論,每發生一次 test() 函數調用,我們就在函數名后加一個后綴“_xx”。

test() 函數首次被調用是在 main() 函數中,進入 test() 函數后,程序會先遞歸調用 test(left, mid); 行此時可寫作:

  • test_0(0, 5):輸出“before:0 2 5”,調用 test_1(0, 2)
  • test_1(0, 2):輸出“before:0 1 2”,調用 test_2(0, 1)
  • test_2(0, 1):輸出“before:0 0 1”,調用 test_3(0, 0)
  • test_3(0, 0):因為 0>=0,所以直接返回 test_2(0, 1)

此時應注意,test_0~3() 都是在執行到“test(left, mid)”時發生遞歸調用的,因此它們將返回到“test(mid+1, right)”處。

返回到 test_2(0, 1) 時,left=0, right=1, mid=0,因此 test(mid+1, right) 會直接返回,輸出“after:0 0 1”;接著返回 test_1(0, 2),同理,輸出“after:0 1 2”;接著返回 test_0(0, 5) 的 test(mid+1, right); 行,此時 left=0, right=5, mid=2,接下來的過程將如下進行:

  • test_4(3, 5):輸出“before:3 4 5”,調用 test_5(3, 4)
  • test_5(3, 4):輸出“before:3 3 4”,調用 test_6(3, 3)
  • test_6(3, 3):因為 3>=3,所以直接返回 test_5(3, 4)

此時應注意 test4~6() 是在執行 “test(mid+1, right)”時發生遞歸調用的,因此它們將返回到“printf("after:...”處。

函數返回到 test_5(3, 4) 后,此時 left=3, right=4, mid=3,因此 輸出“after:3 3 4”,并返回 test_4(3, 5),輸出“after:3 4 5”,并返回 test_0(0, 5),輸出“after:0 2 5”。整理一下,得到上述C語言程序的輸出如下:

 

  1. before: 0 2 5before: 0 1 2before: 0 0 1after : 0 0 1after : 0 1 2before: 3 4 5before: 3 3 4after : 3 3 4after : 3 4 5after : 0 2 5 

可見,從編程語言角度來分析遞歸函數,的確是一件比較吃力的事情,若是輸入給 test(0, 105) 函數的參數更寬一些,基本上可以認為是不可分析的。現在我們嘗試從實際需求角度理解遞歸函數 test() 的功能,應該能夠輕易得到以下信息:

  • 當輸入的 left 不小于 right 時,函數直接返回
  • 否則 test() 函數將打印 left 與 right 以及它倆的平均整數值 mid
  • 接著,令 right=mid,并重復上述過程;令 left=mid+1,并重復上述過程
  • 打印在這之后的 left,mid,right

稍加理解,基本應該能明白 test() 函數的功能了:mid 是 left 和 right 的二分中間值,因此 test(left, mid) 可以看作是二分之后的左半部分,test(mid+1, right) 則可以看作是二分之后的右半部分,因此 test() 函數的作用其實就是在不停的二分 left~right 區間,一直到不能繼續二分為止(left>=right),這一過程是逐步遞歸的,因此 test() 函數也會逐步輸出二分的結果。以分析“after:...”輸出為例,test(0, 5) 會依次輸出:

 

  1. 0 0 10 1 23 3 43 4 50 2 5 

這與從編程語言角度分析的結果是一致的。理解這一點后,現在我們引入應用實例,假定有一個數組 {3,2,5,1,4},調用 test(0,5) 逐步二分該數組,過程應該如下圖所示:

 

C語言的“遞歸函數”這么難理解,為什么不丟棄它呢?

 

二分數組過程

至此,我們便從實際需求角度分析了遞歸函數,可見,理解遞歸函數其實就是理解其設計,站在更高處從大局出發理解其含義的。

小結

“遞歸”不能算是C語言中的語法,它更像是一種思想,因此“C語言為什么不丟棄“遞歸””這種說法是沒有意義的。本文還通過兩個實例,從編程語言和實際應用兩個角度討論了如何分析C語言中的遞歸函數,可見,遞歸有時的確能夠簡潔的解決問題。不過不得不承認,遞歸的確是一個較難理解的概念,而且遞歸函數也比較難以調試,消耗棧空間巨大,因此在實際的C語言程序開發中,除非遞歸能夠帶來極大的便利,否則不建議使用遞歸,而是盡量嘗試使用循環解決問題。

 


 

責任編輯:華軒 來源: 今日頭條
相關推薦

2019-08-30 14:58:47

JavaScript程序員編程語言

2017-01-23 13:08:46

大數據客戶畫像技術

2020-11-10 22:53:54

oracle數據庫

2019-09-29 10:45:46

C語言CPU編譯器

2020-12-08 05:41:46

人工智能人機融合機器學習

2020-02-28 16:10:13

攜號轉網運營商中國電信

2020-12-10 13:37:08

人工智能人機融合

2020-07-02 14:12:52

C++語言編程

2024-07-25 09:10:00

2022-06-12 23:36:26

微服務架構單體應用

2020-11-19 15:34:47

前端招聘開發

2011-05-12 14:57:58

2023-03-26 00:04:14

2018-06-22 07:51:13

2023-05-04 11:39:17

經營分析流量項目

2022-03-31 11:38:09

經營分析傳統企業運營商

2014-10-10 13:46:33

Docker

2019-08-01 07:48:27

物聯網模塊物聯網IOT

2012-11-27 10:36:19

公有云Azure數據中心

2022-09-16 10:14:41

消息順序性分布式架構
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黄视频国产| 国产成人精品一区二区三区 | 一区日韩 | 久久在线 | 91精品国产91久久久久久 | 日韩高清中文字幕 | 免费观看一级毛片视频 | 欧美影院| 国产精品视频一区二区三区不卡 | 一区二区视频在线 | 91麻豆精品国产91久久久资源速度 | 在线欧美视频 | 日韩一二区 | 国产日韩视频在线 | 国产精品99久久久久久大便 | 日日噜噜噜夜夜爽爽狠狠视频97 | 精品久久一区二区 | 国产免费一区二区 | 日韩一级 | 久久久久久国产精品免费免费狐狸 | 一级黄色片在线看 | 天天干视频网 | 色吊丝2288sds中文字幕 | 九九热在线视频 | 91精品国产91久久久久久不卞 | 亚洲在线视频 | 欧美亚洲国产精品 | www.日韩系列 | 成人av鲁丝片一区二区小说 | 亚洲视频免费观看 | 国产在线视频一区二区董小宛性色 | 亚洲国产精品成人无久久精品 | 一本一道久久a久久精品综合蜜臀 | 一级特黄在线 | 久久亚洲欧美日韩精品专区 | 91九色在线观看 | 久久久久久久久久久丰满 | 国产 欧美 日韩 一区 | 少妇精品亚洲一区二区成人 | 99精品久久| 密乳av |