经典基础算法之面试题(系列一)(转)
1. 打靶問題的遞歸解法
Technorati 標記:?遞歸算法,字典法,二分法,打靶問題,日期問題,求冪,多項式求值倫敦奧運會火熱進行中,讓我們來看個打靶的問題:一個射擊運動員打靶,靶一共有10環,求連開10槍打中90環的可能行有多少種?
分析:這是一個典型遞歸求解問題。假設第10槍打x環,則將問題轉換為剩下9槍打90-x環的可能有多少種,x的取值范圍為[0, 10],根據加法原理,則:10槍打90環的可能 = 第10槍打0環,剩下9槍打90環的可能 + 第10槍打1環,剩下9槍打89環的可能 + 第10槍打2環,剩下9槍打88環的可能?
+ 第10槍打3環,剩下9槍打87環的可能 + 第10槍打4環,剩下9槍打86環的可能 + 第10槍打5環,剩下9槍打85環的可能?
+ 第10槍打6環,剩下9槍打84環的可能 + 第10槍打7環,剩下9槍打83環的可能 + 第10槍打8環,剩下9槍打82環的可能
+ 第10槍打9環,剩下9槍打81環的可能 + 第10槍打10環,剩下9槍打80環的可能。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
遞歸的停止條件為:
1. 若環數小于0 或者 剩下的環數大于剩下的槍數乘以10(即剩下每槍打10環用不玩所剩環數),則該遞歸路徑不記入可能情況。
2. 若不滿足條件1,且所剩槍數為1,則該遞歸路徑記為1中可能情況。
2. 求多項式的值與求冪的快速算法
2.1 求多項式的值
已知多項式中的系數a0,a1,a2……an及x的值,求f(x)。
多項式求值問題可以利用輾轉相乘的方式進行計算,根據上面的式子分析,可以將上式轉換為: f(x) = ((((an * x) + an-1) * x + an-2) * x? ……. + a1) * x + a0?
有了這個變形,代碼自然而然就能寫出來了:
2.2 求冪的快速算法
求x的p次冪本是簡單的問題,可以將x次乘p次就可以了,這里我們當然不是要討論這種計算方法,這里討論的是怎樣高效計算。
有沒有可能減少做乘法的次數呢?讓我們來做一個分析,考慮x的6次冪的情況:x * x * x * x * x * x = (x * x * x) * (x * x * x)
等式前面部分需進行6次乘法,后半部分需計算(x * x * x) ,然后乘以上次計算的值即可,共4次乘法。
通過上述分析可知,求x的p次冪可以通過遞歸折半的方法來減少乘法的次數,循著這個思考,實現就不太困難了。
計算2的20次冪總共進行5次乘法就足夠了,而原始的算法需要20次,那么這種算法究竟需要多少次乘法呢?
由于該算法根本思想是折半遞歸,類似于2分查找,所以乘法的次數為lgp取天花板值這個數量級的(lgp表示以2為底p的對數)。
整型int最多表示2的32次冪,因此最多節省24次乘法,對于現代的計算機,這似乎不是特別重要,但是在以下情況下該算法具有重要價值:
1. 對于需要高頻率計算冪的情況;
2. 對于大數高精度計算的情況,如需計算2的10000次冪。
當然了,這也是對遞歸算法和2分法的巧妙應用,學習其思想吧。
3. 一年中的第n天是幾月幾號?
和這個問題類似的問題還有:
1. 給定某年某月某日,問這是這年的第多少天?
2. 已知某年的1月1號是星期幾,求給定的某年某月某日是星期幾?
其實這類問題的共同點在于它們都需要考慮閏年問題,大月小月問題;
首先我們解決閏年問題,根據閏年的定義定義如下宏來判斷某年是否是閏年:
#define IS_LEAP(X) (((X) % 400 == 0 || (X) % 100 != 0 && (X) % 4 == 0) ? 1 : 0)
在來看大月小月問題,可以定義如下二維數組,用day_count_of_month[0][12]表示潤年情況下每月的天數,用day_count_of_month[1][12]表示非潤年情況下每月的天數(這就是傳說中的字典法了,就是根據提供的信息查表,類似于查字典,所以叫做字典法):
int day_count_of_month[2][12] = {
??????????????????????????????????????????????? {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},?
??????????????????????????????????????????????? {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
?????????????????????????????????????????????? };
1: #include <stdio.h> 2: 3: #define IS_LEAP(X) (((X) % 400 == 0 || (X) % 100 != 0 && (X) % 4 == 0) ? 1 : 0)4: int day_count_of_month[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 5: {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; 6: /* 計算year年的第n天是幾月幾號 */7: void convert_to_date(int year, int n) 8: { 9: int leap = IS_LEAP(year); 10: if(year < 1970 || year > 5000 || n <= 0 || n > 365 + leap) { 11: printf("bad year\n"); 12: } 13: 14: int i = 0; 15: for(; n > day_count_of_month[leap][i]; ++i) { 16: n -= day_count_of_month[leap][i]; 17: } 18: 19: printf("year : %d, month : %d, day : %d\n", year, i + 1, n); 20: } 21: 22: int main() 23: { 24: convert_to_date(2012, 230); 25: }上面這段代碼解決了第一個問題,第二個問題可以轉換為類似第一個問題的問題:
先計算給定的某年某月某日到給定已知這天的總天數s=>s%7=>利用模運算結果來推算所求日期的星期情況即可。
?
http://www.cnblogs.com/dskit/archive/2012/08/11/2633736.html
?
轉載于:https://www.cnblogs.com/bizhu/archive/2012/08/19/2646325.html
總結
以上是生活随笔為你收集整理的经典基础算法之面试题(系列一)(转)的全部內容,希望文章能夠幫你解決所遇到的問題。