<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聽說 90 % 的程序員都會遞歸?

          共 3739字,需瀏覽 8分鐘

           ·

          2021-02-13 10:59



          今天的文章主要是從宏觀角度聊聊遞歸這種算法思想,文章主要內(nèi)容如下:
          • 通過數(shù)組求和看遞歸的基本性質(zhì)

          • 鏈表的天然遞歸性

          01

          通過數(shù)組求和看遞歸的基本性質(zhì)

          現(xiàn)在給出一個數(shù)組arr={1,3,6},問如何用遞歸方式求出數(shù)組中所有元素的總和。
          遞歸思想介紹
          假設(shè)現(xiàn)在A拿到了這個問題,但是A只能計算數(shù)組中的第一個元素值和剩余元素總和的和,那這時怎么辦呢?
          A就想,我可以先把數(shù)組中第一個元素1的值記錄下來,然后讓B計算數(shù)組中剩余元素的總和。
          于是,B就收到一個任務(wù),需要計算數(shù)組{3,6}的總和,同樣的B和A一樣也只能計算數(shù)組中的第一個元素值和剩余元素總和的和。因此,B記錄下他拿到的數(shù)組中的第一個元素3的值,然后讓C去計算數(shù)組中剩余元素的總和。
          這時,C收到一個任務(wù),是計算數(shù)組{6}的總和,同樣的C和A一樣也只能計算數(shù)組中的第一個元素值和剩余元素總和的和,并且除了第一個元素外,C不知道數(shù)組中還剩余哪些元素。于是,C將數(shù)組中的第一個元素6的值記錄下來,然后讓D去計算數(shù)組中剩余元素的總和。
          D在收到任務(wù)后,一看數(shù)組是空的,就知道數(shù)組中元素總和是0。
          然后D就告訴C,剩余元素的總和是0。于是C將自己記錄的元素6和D告訴他的剩余元素的總和0相加后得到6,他就把這個結(jié)果告訴了B。
          B在收到C的結(jié)果6后,同樣的將自己記錄的元素3和其相加得到9,于是他就把這個結(jié)果告訴了A。
          A在拿到B告訴他的結(jié)果9后,將其和自己記錄的元素1相加得到結(jié)果10。這時A就知道數(shù)組中所有元素的總和是10。
          上述過程中A拿到求數(shù)組中所有元素和這個問題后,將其告訴B,B再告訴C,C再告訴D,這個過程我們可以稱之為”遞“
          然后,D將結(jié)果告訴C,C再告訴B,B在告訴A的這個過程我們可以稱之為”歸“
          遞歸要滿足的條件
          在上述問題的描述中,其實就包含了遞歸這種算法思想的一些基本條件:
          一是一個問題可以分解為多個更小的子問題并且這多個更小的子問題的求解思路完全一樣
          對于數(shù)組求和這個問題來說,A要知道自己拿到的數(shù)組的總和這個問題,可以分解為B要知道除去數(shù)組第一個元素外剩余元素總和這樣一個問題。
          同時,對于A和B來說,他們求解問題的思路是一樣的,即只計算自己拿到的數(shù)組中第一個元素值和數(shù)組中剩余元素總和的和,而不管數(shù)組中剩余元素總和的和是如何計算的。
          二是存在一個終止條件,可以讓遞歸停下來。
          比如數(shù)組求和的終止條件就是數(shù)組中沒有剩余元素需要計算了。
          數(shù)組求和的遞歸思路具體如下圖:
          遞歸代碼的編寫
          遞歸代碼的編寫一是要知道遞歸終止條件,對于數(shù)組求和這個問題來說,其終止條件是數(shù)組中沒有元素需要計算了,代碼表示是:
          // begin 表示從數(shù)組中哪個索引位置開始//?當(dāng)begin?==?arr.length表示數(shù)組中沒有剩余元素if (begin == arr.length) {    return 0;}

          二是要知道遞歸遞推公式,就數(shù)組求和這個問題來說,遞推公式是:

          arr[begin] + sum(begin + 1, arr);

          有了終止條件和遞推公式,就可以寫出遞歸求解數(shù)組中所有元素和的代碼了,實現(xiàn)如下:

          public int sum (int[] arr) {    // 調(diào)用遞歸函數(shù),初始從0開始    return sum(0, arr);}
          // 遞歸函數(shù)private int sum(int begin, int[] arr) { // begin 表示從數(shù)組中哪個索引位置開始 // 當(dāng)begin == arr.length表示數(shù)組中沒有剩余元素 if (begin == arr.length) { return 0; }
          // 我只計算我拿到的數(shù)組中第一個元素的值 // 和數(shù)組中剩余元素總和的和????return?arr[begin]?+?sum(begin?+?1,?arr);?//?遞歸調(diào)用}

          這里我們在第7行sum(int begin, int[] arr)這個方法中調(diào)用了sum(int begin, int[] arr)方法它自己,即16行的sum(begin + 1, arr),這一點可能會讓你覺得困惑。

          我們可以這樣理解,方法sum(int begin, int[] arr)是計算數(shù)組中從索引begin開始所有元素的總和,而該方法的計算規(guī)則是我計算的是當(dāng)前數(shù)組起始位置begin所對應(yīng)的元素值和數(shù)組中剩余元素的總和的和。那么,我就需要有個方法可以告訴我數(shù)組中剩余元素的總和是多少。

          這時,剛好有個方法fun(int begin, int[] arr),只要我告訴它數(shù)組是什么樣的,以及從哪個索引位置開始計算,它就會告訴我數(shù)組中剩余元素的總和。只不過這個方法fun(int begin, int[] arr)所需的參數(shù)和我自己一樣而已。于是,在代碼中看起來就是方法sum(int begin, int[] arr)調(diào)用了它自己。


          02

          鏈表的天然遞歸性

          接著我們看下如何用遞歸思想解答LeetCode中#203.移除鏈表元素這個問題。

          題目描述

          刪除鏈表中等于給定值 val 的所有節(jié)點。?


          示例:?

          輸入:?1->2->6->3->4->5->6,?val?=?6?

          輸出:?1->2->3->4->5

          LeetCode
          思路分析
          為了方便演示,我將題目示例中給定的鏈表進行了簡化。如下圖:
          先看下”遞“的過程,對于A來說,他在拿到給定的鏈表后,記錄下了頭節(jié)點,然后將頭結(jié)點之后的鏈表給了B。
          同樣的B在拿到給定的鏈表后,記錄下了頭節(jié)點,然后將頭結(jié)點之后的鏈表給了C。
          C在拿到給定的鏈表后,記錄下了頭節(jié)點,然后將頭結(jié)點之后的鏈表給了D。
          D在拿到給定的鏈表后,記錄下了頭節(jié)點,然后將頭結(jié)點之后的鏈表給了E。
          這時E拿到的鏈表是一個空鏈表。
          E拿到空鏈表之后,就將其返回給了D,這時D就知道它的鏈表是3—>null。
          D將自己得到的結(jié)果3—>null返回給了C,這時C就知道它的鏈表是6—>3—>null。
          接著C需要將自己計算的結(jié)果返回給B,但這時C發(fā)現(xiàn)自己拿的鏈表的頭結(jié)點是和要移除的元素val=6相等的,因此C返回B的結(jié)果應(yīng)該是去掉頭結(jié)點6后的剩余部分3—>null。
          B在拿到C返回的結(jié)果后,就知道它的鏈表是2—>3—>null。然后,它將這個結(jié)果告訴了A。
          A在拿到B返回的結(jié)果后,就知道它的鏈表是1—>2—>3—>null,也就是移除鏈表中元素val=6之后的結(jié)果。
          對于上述的遞歸過程,可以簡化為兩個步驟:
          一是記錄下鏈表當(dāng)前頭結(jié)點head,然后調(diào)用移除鏈表中結(jié)點值val=6的方法;
          二是在拿到調(diào)用方法之后的結(jié)果時,判斷一下當(dāng)前記錄的頭結(jié)點的值是不是等于val=6,如果不等,則將調(diào)用方法之后的結(jié)果掛在所記錄的頭結(jié)點之后,返回;如果相等,則直接將調(diào)用方法之后的結(jié)果返回。

          鏈表這種數(shù)據(jù)結(jié)構(gòu),我們說它具有天然遞歸性,就在于對于一個鏈表,我們可以將其分解為頭結(jié)點和一個子鏈表。然后,對于子鏈表同樣可以分解為一個頭結(jié)點和一個子鏈表。這滿足的是遞歸思想中的:一個問題可以分解為多個子問題并且這多個子問題求解思路是一樣的這一條件。
          此外,對于鏈表分解到最后時,頭結(jié)點為null,這滿足的是遞歸思想中的:存在遞歸終止條件,讓遞歸停下來這一條件。
          代碼實現(xiàn)
          public ListNode removeElements(ListNode head, int val) {    // 遞歸終止條件    if (head == null) {        return null;    }
          // 調(diào)用方法removeElements將當(dāng)前結(jié)點之后的鏈表部分中的 // 和val值相等的結(jié)點刪除 ListNode result = removeElements(head.next, val);
          // 如果當(dāng)前結(jié)點head的值等于val則返回result if (head.val == val) { return result; } else { // 如果當(dāng)前結(jié)點head的值不等于val則 // 將result掛在當(dāng)前結(jié)點head之后返回 head.next = result; return head; }}


          良許個人微信


          添加良許個人微信即送3套程序員必讀資料


          → 精選技術(shù)資料共享

          → 高手如云交流社群





          本公眾號全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲取!

          推薦閱讀:

          親歷:一行代碼,百萬人民幣打水漂!

          “華為天才少年”自制百大Up獎杯,網(wǎng)友:技術(shù)難度不高侮辱性極強

          我勸你不要再留QQ郵箱了


          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復(fù)「1024」,即可免費獲取!!


          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩网址在线 | 欧美黄A片免费视频www | 骚逼美女 | 夜夜撸狠狠撸 | 91沈先生探花在线 |