如何才能轻而易举的写出递归函数
先說下我的學習方式吧
首先,我想說下我的學習方式,每個人的學習方式都是不同的,有的人悟性比較高,所以學東西比較快,但也有一部分人,學東西比較慢,哈哈,我就是那學東西比較慢的那一部分人。但我也不氣餒,認真踏實的學就可以了,千萬不能浮躁。
所以在我學習算法的過程中,首先要搞懂這個技術是來解決什么問題的,這是很重要的一步,其次如何使用,最后它的原理是什么。走好這三步我覺得這個技術你也就學會了。
遞歸
首先第一步,遞歸這個算法是用來解決什么問題的或者主要用于什么方向的。在我目前的理解,遞歸主要是將一個大問題分解成小問題,最后各個小問題的解再進行合并。這就是遞歸的主要作用。
注意兩個技巧來編寫遞歸函數:
- 1.思考這個問題能否分解成小問題
- 2.遞歸函數的編寫在我看來最重要的是定義遞歸函數的功能,不要太糾結遞歸函數是怎么運行的,這么思考會陷入死胡同的,我之前也是糾結這塊,弄得頭疼。
做幾道題目吧(leetcode 343,leetcode 337)
leetcode 343:給定一個正整數 n,將其拆分為至少兩個正整數的和,并使這些整數的乘積最大化。 返回你可以獲得的最大乘積。
實例:輸入: 10
輸出: 36
解釋: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
首先如何思考題目:說至少拆分成兩個數,那么拆分出來的兩個數是否又可以進行拆分?
就好比10拆分成10=4+6,那么6是否可以拆分成4+2?是不是有一點激動,是不是很像遞歸,將一個大問題拆分成一個個小問題。
那么知道了方法,那思路是怎么的呢?我們可以將一個數從1進行拆分,一直到n-1。打個比方吧,比如10,我們一個個拆分成【1,9】,【2,8】,【3,7】…【9,1】,那么在拆分的過程中,比如拆分成【1,9】,9是否又可以進行拆分?是重復的一個過程,但就是這個過程我們定義成了遞歸函數,所以遞歸函數的功能就是給你一個數返回它的乘積最大化結果。
那我們現在就來定義遞歸函數的功能:
上面那道題就完成了,但是寫成那樣,時間復雜度比較高,應該不可能通過所有測試用例,所以我們要利用HashMap<Integer,Integer> 來記錄一個正整數的最大化乘積的值,避免重復計算。這種方式就是備忘錄方法。
HashMap<Integer,Integer> map=new HashMap<>(); public int help(int num){ if(num==2){ return 1;}//主要添加了下面三行代碼,你們想要,我求出了10的最大化,//是不是要進行保存起來,因為10的最大化是一個固定值,不是變化的,所以當再用到10這個正整數的時候//就無須再進行計算了,直接返回他的結果就可以了if(map.containsKey(num)){return map.get(num);}int res=0;for(int i=1;i<num;i++){//將num拆分成i和num-iint a=i*help(num-i);int b=i*(num-i); res=Math.max(res,Math.max(a,b)); }//將num這個正整數的最大化乘積res進行保存map.put(num,res);return res; }到目前為止,這道題才算是解決了。
接著再來一題,
leetcode 337:在上次打劫完一條街道之后和一圈房屋后,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,我們稱之為“根”。 除了“根”之外,每棟房子有且只有一個“父“房子與之相連。一番偵察之后,聰明的小偷意識到“這個地方的所有房屋的排列類似于一棵二叉樹”。 如果兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。
計算在不觸動警報的情況下,小偷一晚能夠盜取的最高金額。
這一題如何思考呢?在我認為遞歸最要緊的就是定義函數功能,這一題的遞歸函數功能是什么呢?那就是給你一個二叉樹中的任意一個節點,將它當做根節點,返回此節點的最大盜取金額。比如上一張圖片中的4,如果傳遞的節點是4,那我們要返回的就是以4為根節點,小偷在以4為根節點的樹上,能盜取的最大金額是多少,當然為了便于理解,3,5,1節點可以舍棄不看。有了這樣的思路,我們來編寫程序。
再次強調,必須要定義好遞歸函數的功能,**rob(root.left.left)**返回的就是以根節點的左孩子的左孩子為根節點小偷能盜取的最大值,定義遞歸函數功能尤其重要。
畫張圖就能更好地理解了:
不過上面的遞歸時間復雜度是有點高的,大家可以想想,以某個節點為根節點,那么能盜取的最大金額是固定的,所以我們可以維護一個HashMap<TreeNode,Integer>來做備忘錄。代碼就不寫了,很簡單,和第一題的備忘錄幾乎一樣,大家可以動手試試。
總結:
遞歸函數在我看來最重要的是要定義遞歸函數的功能,這個最為重要,其次就是要明白遞歸是一個不斷重復的子過程。不過我們也可以在此基礎上畫圖來更加深入地理解遞歸。舉個簡單的例子,如何用遞歸求一個數組的最大值?我們可以將數組一分為2,左邊求最大值,右邊求最大值,然后比較返回,此時的遞歸函數功能是啥?就是給你一個數組中的指定范圍,求出指定范圍的數組最大值。我們可以舉個簡單例子,比如只有4個元素的數組,可以進行畫圖來更加深入的理解上面的遞歸思想。不過話說回來,每個人都有每個人的看法,大家可以多思考,可以按照文章中提出的方法多寫幾道遞歸的題目,總有一天會讓你感受到遞歸的思想。遞歸是很強大的,比如我們使用的排序,例如歸并排序,快速排序都離不開遞歸。
這邊可以稍微提一下兩個排序的思想:歸并排序就是將數組一分為2,左邊先排好序,右邊排好序,然后再合并。那歸并排序的遞歸函數功能是什么呢?就是排序。我們要做的就是先將數組一分為2,左半部分調用遞歸有序,右半部分調用遞歸有序,我們最后要做的就是將兩個有序數組合并成一個有序數組。
那么快速排序的怎么理解呢?快速排序涉及到荷蘭國旗問題,就是隨機選取一個數,將數組中小于這個數的數字放在左邊,大于這個數的數字放在右邊,接著再對左右兩邊繼續遞歸。這兩個排序加堆排序我也會進行整理總結,畢竟他們在面試中經常被提問,還是比較重要的。
這是我第一次寫博客,可能有些語句不通順或者邏輯不通,還請大家見諒,也希望大家能多多留言,我們一起進步。
總結
以上是生活随笔為你收集整理的如何才能轻而易举的写出递归函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器操作系统锁定设置,服务器操作系统锁
- 下一篇: 树结构之树和二叉树的概念以及如何用面向对