java面試算法題總結

轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/8035261
1、快速找出一個數組中的最大數、第二大數。

     思路:如果當前元素大於最大數 max,則讓第二大數等於原來的最大數 max,再把當前元素的值賦給 max。如果當前的元素大於等於第二大數secondMax的值而小於最大數max的值,則要把當前元素的值賦給 secondMax。
  1. void GetSecondMaxNumber(int *arr , int n)  
  2. {  
  3.     int i , max , second_max;  
  4.     max = arr[0];  
  5.     second_max = 0x80000000;  
  6.     for(i = 1 ; i < n ; ++i)  
  7.     {  
  8.         if(arr[i] > max)  
  9.         {  
  10.             second_max = max;  
  11.             max = arr[i];  
  12.         }  
  13.         else  
  14.         {  
  15.             if(arr[i] > second_max)  
  16.                 second_max = arr[i];  
  17.         }  
  18.     }  
  19.     cout<<max<<"  "<<second_max<<endl;  
  20. }  

2、試着用最小的比較次數去尋找數組中的最大值和最小值。

解法一:
掃描一次數組找出最大值;再掃描一次數組找出最小值。
比較次數2N-2
轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/8035261
解法二:
將數組中相鄰的兩個數分在一組, 每次比較兩個相鄰的數,將較大值交換至這兩個數的左邊,較小值放於右邊。
對大者組掃描一次找出最大值,對小者組掃描一次找出最小值。
比較1.5N-2次,但需要改變數組結構
 
解法三:
每次比較相鄰兩個數,較大者與MAX比較,較小者與MIN比較,找出最大值和最小值。
方法如下:先將一對元素互相進行比較,然後把最小值跟當前最小值進行比較,把最大值跟當前最大值進行比較。因此每兩個元素需要3次比較。如果n爲奇數,那麼比較的次數是3*(n/2)次比較。如果n爲偶數,那麼比較的次數是3n/2-2次比較。因此,不管是n是奇數還是偶數,比較的次數至多是3*(n/2),具體的代碼如下:
  1. void GetMaxAndMin(int *arr , int n , int &max , int &min)  
  2. {  
  3.     int i = 0 ;  
  4.     if(n & 1)     // 奇數  
  5.     {  
  6.         max = min = arr[i++];  
  7.     }  
  8.     else  
  9.     {  
  10.         if(arr[0] > arr[1])  
  11.         {  
  12.             max = arr[0];  
  13.             min = arr[1];  
  14.         }  
  15.         else  
  16.         {  
  17.             max = arr[1];  
  18.             min = arr[0];  
  19.         }  
  20.         i += 2;  
  21.     }  
  22.       
  23.     for( ; i < n ; i += 2)  
  24.     {  
  25.         if(arr[i] > arr[i+1])  
  26.         {  
  27.             if(arr[i] > max)  
  28.                 max = arr[i];  
  29.             if(arr[i+1] < min)  
  30.                 min = arr[i+1];  
  31.         }  
  32.         else  
  33.         {  
  34.             if(arr[i+1] > max)  
  35.                 max = arr[i+1];  
  36.             if(arr[i] < min)  
  37.                 min = arr[i];  
  38.         }  
  39.     }  
  40. }  

3、重排問題

給定含有n個元素的整型數組a,其中包括0元素和非0元素,對數組進行排序,要求:
1、排序後所有0元素在前,所有非零元素在後,且非零元素排序前後相對位置不變
2、不能使用額外存儲空間
例子如下
輸入 0、3、0、2、1、0、0
輸出 0、0、0、0、3、2、1
分析
此排序非傳統意義上的排序,因爲它要求排序前後非0元素的相對位置不變,或許叫做整理會更恰當一些。我們可以從後向前遍歷整個數組,遇到某個位置i上的元素是非0元素時,如果arr[k]爲0,則將arr[i]賦值給arr[k],arr[i]賦值爲0。實際上i是非0元素的下標,而k是0元素的下標。
  1. void Arrange(int *arr , int n)  
  2. {  
  3.     int i , k = n-1;  
  4.     for(i = n-1 ; i >=0 ; --i)  
  5.     {  
  6.         if(arr[i] != 0)  
  7.         {  
  8.             if(arr[k] == 0)  
  9.             {  
  10.                 arr[k] = arr[i];  
  11.                 arr[i] = 0;  
  12.             }  
  13.             --k;  
  14.         }  
  15.     }  
  16. }  

4、找出絕對值最小的元素

給定一個有序整數序列(非遞減序),可能包含負數,找出其中絕對值最小的元素,比如給定序列 -5、-3、-1、2、8 則返回1。
分析:由於給定序列是有序的,而這又是搜索問題,所以首先想到二分搜索法,只不過這個二分法比普通的二分法稍微麻煩點,可以分爲下面幾種情況
    如果給定的序列中所有的數都是正數,那麼數組的第一個元素即是結果。
    如果給定的序列中所有的數都是負數,那麼數組的最後一個元素即是結果。
    如果給定的序列中既有正數又有負數,那麼絕對值的最小值一定出現在正數和負數的分界處。
爲什麼?因爲對於負數序列來說,右側的數字比左側的數字絕對值小,如上面的-5、-3、-1,而對於整整數來說,左邊的數字絕對值小,比如上面的2、8,將這個思想用於二分搜索,可先判斷中間元素和兩側元素的符號,然後根據符號決定搜索區間,逐步縮小搜索區間,直到只剩下兩個元素。
單獨設置一個函數用來判斷兩個整數的符號是否相同

  1. bool SameSign(int m , int n)  
  2. {  
  3.     if((m>>31) == (n>>31))  
  4.         return true;  
  5.     else  
  6.         return false;  
  7. }  
  8.   
  9. // 找出一個非遞減序整數序列中絕對值最小的數  
  10. int MiniNumAbsoluteValue(int *arr , int n)  
  11. {  
  12.     if(n == 1)  
  13.         return arr[0];  
  14.     if( SameSign(arr[0] , arr[n-1]) )  
  15.         return arr[0] >= 0 ? arr[0] : abs(arr[n-1]);  
  16.   
  17.     // 二分搜索  
  18.     int mid , low = 0 , high = n - 1;  
  19.     while(low < high)  
  20.     {  
  21.         if(low + 1 == high)  
  22.             return abs(arr[low]) < abs(arr[high]) ? abs(arr[low]) : abs(arr[high]);  
  23.         mid = (low + high)>>1;  
  24.         if( SameSign(arr[low] , arr[mid]) )  
  25.         {  
  26.             low = mid ;     // 有可能分界點就在mid處  
  27.             continue;  
  28.         }  
  29.         if( SameSign(arr[high] , arr[mid]) )  
  30.         {  
  31.             high = mid;  
  32.             continue;  
  33.         }  
  34.     }  
  35.     return abs(arr[low]);  
  36. }  
5、一道經典的額遞歸題目
函數 int func(int i ,int N);
其中i <= N,功能輸出i遞增到N再遞減到i的整數,每行輸出一個數。比如func(1,5)就是
1
2
3
4
5
4
3
2
1
要求:
1、只能有1個語句,即一個分號
2、不能使用do while until goto for if關鍵字,不能使用?:和逗號運算符
3、唯一能使用的庫函數爲printf 
  1. <span style="font-family:Verdana; font-size:14px">int func(int i , int n)  
  2. {  
  3.     return (i==n && printf("%d\n",i)) || (printf("%d\n",i) && func(i+1,n) && printf("%d\n",i));  
  4. }</span>  
6、從長度爲n的數組(元素互不相同)中任意選擇m個數的所有組合。
DFS
  1. /**   
  2. ** author :hackbuteer 
  3. ** date   :2012-10-01    
  4. **/  
  5. #include<iostream>  
  6. #include<vector>  
  7. using namespace std;  
  8.   
  9. int arr[] = {1,2,3,4,5,6,7,8,9,10};  
  10. int len = 10 , m = 3;  
  11.   
  12. void dfs(int num , vector<int>& vec , int curnum , int id)  
  13. {  
  14.     int i ;  
  15.     if(curnum == num)  
  16.     {  
  17.         static int k = 1 ;  
  18.         cout<<k++<<": ";  
  19.         for( i = 0; i < vec.size() ; ++i)  
  20.             cout<<vec[i]<<" ";  
  21.         cout<<endl;  
  22.         return;  
  23.     }  
  24.   
  25.     for( i = id; i < len; ++i)  
  26.     {  
  27.         vec.push_back(arr[i]);  
  28.         dfs(num , vec , curnum + 1 , i + 1);  
  29.         vec.pop_back();  
  30.     }  
  31. }  
  32.   
  33. int main(void)  
  34. {  
  35.     vector<int>vec;  
  36.     dfs(m , vec , 0 , 0);  
  37.     return 0;  
  38. }  
  1. int arr[] = {1,2,3,4,5,8,5,8,9,10};  
  2. int len = 10 , m = 3;  
  3.   
  4. void dfs(int index , int num , vector<int> &vec)  
  5. {  
  6.     int i ;  
  7.     if(index == len+1)  
  8.         return ;  
  9.     if(num == 0)  
  10.     {  
  11.         static int k = 1 ;  
  12.         cout<<k++<<": ";  
  13.         for( i = 0; i < vec.size() ; ++i)  
  14.             cout<<vec[i]<<" ";  
  15.         cout<<endl;  
  16.         return;  
  17.     }  
  18.     vec.push_back(arr[index]);    // 選取第index個元素  
  19.     dfs(index + 1 , num - 1 , vec);  
  20.     vec.pop_back();               // 不選取第index個元素  
  21.     dfs(index + 1 , num , vec);  
  22. }  
  23.   
  24. int main(void)  
  25. {  
  26.     vector<int>vec;  
  27.     dfs(0 , m , vec);  
  28.     return 0;  
  29. }  
7、從長度爲n的數組(元素有可能相同)中任意選擇m個數的所有組合。
先對數組進行排序,然後設置一個標記pre,記錄前一個選擇的數字,然後進行比較。

  1. /**   
  2. ** author :hackbuteer 
  3. ** date   :2012-10-01    
  4. **/  
  5. #include<iostream>  
  6. #include<vector>  
  7. #include<algorithm>  
  8. using namespace std;  
  9.   
  10. int arr[] = {1,2,3,4,5,8,5,8,9,10};  
  11. int len = 10 , m = 3;  
  12.   
  13. void dfs(int num , vector<int>& vec , int curnum , int id)  
  14. {  
  15.     int i , pre = -1;  
  16.     if(curnum == num)  
  17.     {  
  18.         static int k = 1 ;  
  19.         cout<<k++<<": ";  
  20.         for( i = 0; i < vec.size() ; ++i)  
  21.             cout<<vec[i]<<" ";  
  22.         cout<<endl;  
  23.         return;  
  24.     }  
  25.   
  26.     for( i = id; i < len; ++i)  
  27.     {  
  28.         if(pre == -1 || arr[i] > pre)  
  29.         {  
  30.             vec.push_back(arr[i]);  
  31.             dfs(num , vec , curnum + 1 , i + 1);  
  32.             vec.pop_back();  
  33.             pre = arr[i];  
  34.         }  
  35.     }  
  36. }  
  37.   
  38. int main(void)  
  39. {  
  40.     vector<int>vec;  
  41.     sort(arr , arr + len );  
  42.     dfs(m , vec , 0 , 0);  
  43.     return 0;  
  44. }  
8、三色旗排序問題
一個字符串Color,其中每個元素值爲‘R‘’G’‘B’三者之一,實現把數組中元素重新排列爲紅、綠、藍的順序,所有紅色在前,綠色其後,藍色最後,要如何移動次數纔會最少,編寫這麼一個程序。
問題的解法很簡單,您可以自己想像一下在移動旗子,從繩子開頭進行,遇到紅色往前移,遇到綠色留在中間,遇到藍色往後移。
設有三個指針rindex、gindex、bindex,其中gindex來遍歷這個數組序列
1、gindex指向G的時候,gindex++,
2、gindex指向R的時候,與rindex交換,而後gindex++,rindex++,
3、gindex指向B的時候,與bindex交換,而後,gindex不動,bindex--。
    爲什麼,第三步,gindex指向B的時候,與bindex交換之後,gindex不動。
    因爲你最終的目的是爲了讓R、G、B成爲有序排列,試想,如果第三步,gindex與bindex交換之前,萬一bindex指向的是R,而gindex交換之後,gindex此刻指向的是R了,此時,gindex能動麼?不能動啊,指向的是R,還得與rindex交換。

  1. // 三色旗排序問題  
  2. // char str[] = "RGRBRB";  
  3. void mysort(char *str , int n)  
  4. {  
  5.     int rindex = 0 , gindex = 0 , bindex = n - 1 ;  
  6.     while(gindex <= bindex)  
  7.     {  
  8.         if(str[gindex] == 'G')  
  9.             ++gindex;  
  10.         else if(str[gindex] == 'R')  
  11.         {  
  12.             swap(str[gindex] , str[rindex]);  
  13.             ++rindex , ++gindex;  
  14.         }  
  15.         else           // str[gindex] == 'B'  
  16.         {  
  17.             swap(str[gindex] , str[bindex]);  
  18.             --bindex;  
  19.             //++gindex;  
  20.         }  
  21.     }  
  22. }  
9、一個整型數組,含有N個數,將這N個數分成連續的M段,使得這M段每段的和中的最大值最小,輸出最小值。(1<=N<=100000,1<=M<=N,每個數在1到10000之間)   POJ  3273
解題思路:不管分成多少段,每種分法和的最大值都在N個數的最大值和N個數的和之間,所求答案也在這之間。

我們可以以此爲上下界,二分M段和的最大值進行嘗試。對每次二分的值,將數組掃描累加。若當前和大於二分的這個值,則段數加一,由此統計出在當前值下,N個數能夠分成的最小段數。若這個段數小於或等於M,則上界變爲mid-1,並記下當前mid的值。否則,下界變爲mid+1。繼續二分,直到退出循環。最後記錄的low值即爲答案。
  1. #include<iostream>  
  2. #include<cstdio>  
  3. using namespace std;  
  4.   
  5. int m , n , a[100001];  
  6.   
  7. int bsearch(int low , int high)  
  8. {  
  9.     int i , mid , group , sum;  
  10.     while(low <= high)  
  11.     {  
  12.         mid = (low + high)>>1;  
  13.         sum = 0 ,  group = 1;  
  14.         for(i = 0 ; i < n ; ++i)  
  15.         {  
  16.             if(sum + a[i] <= mid)  
  17.                 sum += a[i];  
  18.             else  
  19.             {  
  20.                 group++;  
  21.                 sum = a[i];  
  22.             }  
  23.         }  
  24.         if(group <= m)  
  25.             high = mid - 1 ;  
  26.         else if(group > m)  
  27.             low = mid + 1;  
  28.     }  
  29.     return low;  
  30. }  
  31.   
  32. int main(void)  
  33. {  
  34.     int i , max , sum;  
  35.     while(scanf("%d %d",&n,&m)!=EOF)  
  36.     {  
  37.         max = 0x80000000 , sum = 0;  
  38.         for(i = 0 ; i < n ; ++i)  
  39.         {  
  40.             scanf("%d",&a[i]);  
  41.             sum += a[i];  
  42.             if(a[i] > max)  
  43.                 max = a[i];  
  44.         }  
  45.         printf("%d\n",bsearch(max, sum));  
  46.     }  
  47.     return 0;  
  48. }  
10、一個int數組,裏面數據無任何限制,要求求出所有這樣的數a[i],其左邊的數都小於等於它,右邊的數都大於等於它。
能否只用一個額外數組和少量其它空間實現。
分析:最原始的方法是檢查每一個數 array[i] ,看是否左邊的數都小於等於它,右邊的數都大於等於它。這樣做的話,要找出所有這樣的數,時間複雜度爲O(N^2)。
其實可以有更簡單的方法,我們使用額外數組,比如rightMin[],來幫我們記錄原始數組array[i]右邊(包括自己)的最小值。假如原始數組爲: array[] = {7, 10, 2, 6, 19, 22, 32}, 那麼rightMin[] = {2, 2, 2, 6, 19, 22, 32}. 也就是說,7右邊的最小值爲2, 2右邊的最小值也是2。
有了這樣一個額外數組,當我們從頭開始遍歷原始數組時,我們保存一個當前最大值 max, 如果當前最大值剛好等於rightMin[i], 那麼這個最大值一定滿足條件,還是剛纔的例子。
第一個值是7,最大值也是7,因爲7 不等於 2, 繼續,
第二個值是10,最大值變成了10,但是10也不等於2,繼續,
第三個值是2,最大值是10,但是10也不等於2,繼續,
第四個值是6,最大值是10,但是10不等於6,繼續,
第五個值是19,最大值變成了19,而且19也等於當前rightMin[4] = 19, 所以,滿足條件。如此繼續下去,後面的幾個都滿足。
  1. int arr[100] , rightMin[100];  
  2. void smallLarge(int *arr , int *rightMin , int n)  
  3. {  
  4.     int i , leftMax;  
  5.     rightMin[n - 1] = arr[n - 1];  
  6.     for(i = n - 2 ; i >= 0 ; --i)  
  7.     {  
  8.         if(arr[i] < arr[i+1])  
  9.             rightMin[i] = arr[i];  
  10.         else  
  11.             rightMin[i] = rightMin[i + 1];  
  12.     }  
  13.     leftMax = 0x80000000;  
  14.     for(i = 0 ; i < n ; ++i)  
  15.     {  
  16.         if(arr[i] >= leftMax)  
  17.         {  
  18.             leftMax = arr[i];  
  19.             if(leftMax == rightMin[i])  
  20.                 printf("%d\n",leftMax);  
  21.         }  
  22.     }  
  23. }  
 11、整數的拆分問題
如,對於正整數n=6,可以拆分爲:
6
5+1
4+2, 4+1+1
3+3, 3+2+1, 3+1+1+1
2+2+2, 2+2+1+1, 2+1+1+1+1
1+1+1+1+1+1+1
現在的問題是,對於給定的正整數n,程序輸出該整數的拆分種類數(HDOJ  1028)。

DP思路:
n = n1 + n2 + n3 + n4 + .... + nk
狀態表示:將n劃分爲k個數相加的組合方案數記爲 q(n,k)。(相當於將n個蘋果放入k個盤子)
狀態轉移:
(1)若k>n,則盤子數大於蘋果數,至少有k-n個空盤子,可以將其拿掉,對組合方案數無影響。
q(n,k) = q(n,n)
(2)若k<=n,則盤子數小於等於蘋果數,則分爲兩種情況
1.至少有一個盤子空着:q(n,k) = q(n,k-1)
2.所有盤子都不空:q(n,k) = q(n-k,k)
q(n,k) = q(n,k-1) + q(n-k,k)
方法一:DP非遞歸
  1. int main(void)  
  2. {  
  3.     int n,i,j,dp[121][121];  
  4.     for(i = 1 ; i < 121 ; ++i)  
  5.     {  
  6.         for(j = 1 ; j < 121 ; ++j)  
  7.         {  
  8.             if(i == 1 ||  j == 1)  
  9.                 dp[i][j] = 1;  
  10.             else if(i > j)  
  11.                 dp[i][j] = dp[i][j-1] + dp[i-j][j];  
  12.             else if(i == j)  
  13.                 dp[i][j] = dp[i][j-1] + 1;  
  14.             else  
  15.                 dp[i][j] = dp[i][i];  
  16.         }  
  17.     }  
  18.   
  19.     while(scanf("%d",&n)!=EOF)  
  20.     {  
  21.         cout<<dp[n][n]<<endl;  
  22.     }  
  23.     return 0;  
  24. }  
方法二:遞歸思路
  1. int q(int n , int m)  
  2. {  
  3.     if(n < 1 || m < 1)  
  4.         return 0;  
  5.     if(n == 1 || m == 1)  
  6.         return 1;  
  7.     if(n < m)  
  8.         return q(n , n);  
  9.     if(n == m)  
  10.         return q(n , m - 1) + 1;  
  11.     return q(n , m - 1) + q(n - m , m);  
  12. }  
  13.   
  14. int main(void)  
  15. {  
  16.     int n;  
  17.     while(scanf("%d",&n)!=EOF)  
  18.     {  
  19.         cout<<q(n,n)<<endl;  
  20.     }  
  21.     return 0;  
  22. }  
12、整數的拆分問題
接着上一題,輸出整數的所有劃分方案
  1. void dfs(int sum , vector<int>& vec , int curnum , int id)  
  2. {  
  3.     int i;  
  4.     if(curnum == sum)  
  5.     {  
  6.         static int inum = 1 ;  
  7.         cout<<"方案 "<<inum++<<": ";  
  8.         for(i = 0; i < vec.size() ; ++i)  
  9.             cout<<vec[i]<<" ";  
  10.         cout<<endl;  
  11.         return;  
  12.     }  
  13.   
  14.     for(i = id ; i <= sum; ++i)  
  15.     {  
  16.         if(curnum + i <= sum)  
  17.         {  
  18.             vec.push_back(i);  
  19.             dfs(sum , vec , curnum + i , i);  
  20.             vec.pop_back();  
  21.         }  
  22.         else  
  23.             break;  
  24.     }  
  25. }  
  26.   
  27. void main()  
  28. {  
  29.     vector<int> vec;  
  30.     dfs(10 , vec , 0 , 1);  
  31. }  
13、在數組中尋找和爲給定值的組合
思路:
代碼
14、字符串移動
字符串爲*號和26個字母、阿拉伯數字的任意組合,把*號都移動到最左側,把其他字母和數字都移到最右側並保持相對順序不變,返回字符*的個數,要求時間和空間複雜度最小。
第一種方法:跟上面的重排問題是一樣的
  1. int MoveStar(char *str , int n)  
  2. {  
  3.     int i , j = n-1;  
  4.     for(i = n - 1 ; i >= 0 ; --i)  
  5.     {  
  6.         if(str[i] != '*')  
  7.         {  
  8.             if(str[j] == '*')  
  9.             {  
  10.                 str[j] = str[i];  
  11.                 str[i] = '*';  
  12.             }  
  13.             --j;  
  14.         }  
  15.     }  
  16.     return j+1;  
  17. }  
第二種方法:
  1. int MoveStar(char *str , int n)  
  2. {  
  3.     int i , count = 0;  
  4.     for(i = n - 1 ; i >= 0 ; --i)  
  5.     {  
  6.         if(str[i] == '*')  
  7.             ++count;  
  8.         else if(count > 0)    // str[i] != '*'  
  9.             str[i + count] = str[i];  
  10.     }  
  11.     for(i = 0 ; i < count ; ++i)  
  12.         str[i] = '*';  
  13.     return count;  
  14. }  
時間複雜度O(N),空間複雜度O(1)
15、求數組中兩個元素差的最大值
後面的元素減去前面的元素(你可以認爲你在炒股票,買入價格和賣出價格就是你的盈利),要求:O(N)時間複雜度,O(1)空間複雜度 
思路:首先從包含兩個元素的數組開始考慮問題,當這個包含兩個元素的問題解決了,那麼加一個新的元素會造成什麼影響?要改變哪些值?每次都添加一個元素,每次都將這些可能的改變考慮進來,這樣就能做到遍歷整個數組的時候,問題就解決了。
  1. // 後面的元素減去前面的元素 差的最大值  
  2. int max_difference(int *arr , int n)  
  3. {  
  4.     if(arr == NULL || n < 2)    // 非法輸入  
  5.         return 0;  
  6.     int min = arr[0];  
  7.     int maxDiff = arr[1] - arr[0];  
  8.     for(int i = 2 ; i < n ; ++i)  
  9.     {  
  10.         if(arr[i-1] < min)  
  11.             min = arr[i-1];  
  12.         if(arr[i] - min > maxDiff)  
  13.             maxDiff = arr[i] - min;  
  14.     }  
  15.     return maxDiff;  
  16. }  
16、求數組中兩個元素差的最大值
前面的元素減去後面的元素,要求:O(N)時間複雜度,O(1)空間複雜度
思路:跟上一題的思路很相近
  1. // 前面的元素減去後面的元素 差的最大值  
  2. int max_difference(int *arr , int n)  
  3. {  
  4.     if(arr == NULL || n < 2)    // 非法輸入  
  5.         return 0;  
  6.     int max = arr[0];  
  7.     int maxDiff = arr[0] - arr[1];  
  8.     for(int i = 2 ; i < n ; ++i)  
  9.     {  
  10.         if(arr[i-1] > max)  
  11.             max = arr[i-1];  
  12.         if(max - arr[i] > maxDiff)  
  13.             maxDiff = max - arr[i];  
  14.     }  
  15.     return maxDiff;  
  16. }  
17、輸入一個正數 n,輸出所有和爲 n 連續正數序列。 
例如輸入 15,由於 1+2+3+4+5=4+5+6=7+8=15,所以輸出 3 個連續序列 1-5、4-6 和 7-8。 
方法一:
可以發現任意自然數序列其實是公差爲1的等差數列,假設從i開始的連續k個數的和爲n,即[i , i+k-1],則n=k*(2*i+k-1)/2,所以轉化爲一元二次方程爲:k*k+(2*i-1)*k-2*n=0,解得k=(1-2*i+sqrt((2*i-1)*(2*i-1)+8*n))/2
要滿足k爲整數,根號裏面的肯定必須是平方數,且分子爲偶數,否則k就是小數。
  1. //轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/8035261  
  2. //輸入一個正數 n,輸出所有和爲n 連續正數序列。  
  3. void plusSequence(int n)  
  4. {  
  5.     int i , j , k , m;  
  6.     double num;  
  7.     for(i = 1 ; i <= n/2 ; ++i)  
  8.     {  
  9.         m = (2*i-1)*(2*i-1) + 8*n;  
  10.         num = sqrt(m * 1.0);  
  11.         if(num != (int)num)  
  12.             continue;  
  13.         k = 1 - 2*i + (int)num;  
  14.         if(0 == (k&1) && k > 0)  
  15.         {  
  16.             for(j = 0 ; j < k/2 ; ++j)  
  17.                 printf("%d",i + j);  
  18.             printf("\n");  
  19.         }  
  20.     }  
  21. }  
方法二:
我們知道:
1+2 = 3;
4+5 = 9;
2+3+4 = 9。
等式的左邊都是兩個以上連續的自然數相加,那麼是不是所有的整數都可以寫成這樣的形式呢?稍微考慮一下,我們發現,4和8等數不能寫成這樣的形式。
問題1:寫一個程序,對於一個64位的正整數,輸出它所有可能的連續自然數(兩個以上)之和的算式。
問題2:大家在測試上面的程序的過程中,肯定會注意到有一些數字不能表達爲一系列連續的自然數之和,例如32好像就找不到。那麼,這樣的數字有什麼規律呢?能否證明你的結論?
問題3:在64位正整數範圍內,子序列數目最多的數是哪一個?這個問題要用程序蠻力搜索,恐怕要運行很長時間,能夠用數學知識推導出來?
問題1解答:對於任意的正整數n >= 3(1和2均不能寫成連續的自然數序列之和)。假設n能夠寫成自然數序列[seqStart, seqEnd]之和,則有(seqEnd + seqStart)*(seqEnd - seqStart + 1) = 2*n。考慮左式是兩個整數之積,想到對右邊的2*n進行因數分解,不妨假定2*n = minFactor * maxFactor,則有
seqEnd + seqStart = maxFactor           (1)
seqEnd - seqStart = minFactor-1         (2)
解方程組(1)(2)得:
seqStart = (maxFactor - minFactor + 1) / 2
seqEnd = (maxFactor + minFactor - 1) / 2
因爲maxFactor - minFactor與maxFactor + minFactor有相同的奇偶性,因此只需要判斷maxFactor + minFactor的奇偶性即可,如果maxFactor + minFactor爲奇數,那麼seqStart和seqEnd不是分數,是整數,即這個序列存在。下面是代碼:
  1. //轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/8035261  
  2. //輸入一個正數 n,輸出所有和爲n 連續正數序列。  
  3. void plusSequence(int n)  
  4. {  
  5.     int i , minFactor , maxFactor , start , end;  
  6.     int sqrtn = sqrt(2.0*n);  
  7.   
  8.     for(i = 2 ; i <= sqrtn ; ++i)  
  9.     {  
  10.         if(2*n % i == 0)  
  11.         {  
  12.             minFactor = i;  
  13.             maxFactor = 2*n/i;  
  14.             if(((minFactor + maxFactor)&1) == 1)     //奇數  
  15.             {  
  16.                 start = (maxFactor - minFactor + 1)>>1;  
  17.                 end = (maxFactor + minFactor - 1)>>1;  
  18.                 printf("%d %d",start,end);  
  19.                 printf("\n");  
  20.             }  
  21.         }  
  22.     }  
  23. }  



轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/8035261

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great":
    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".
    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t
We say that "rgeat" is a scrambled string of "great".
Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".
    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a
We say that "rgtae" is a scrambled string of "great".
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

 


分析:

這個問題是google的面試題。由於一個字符串有很多種二叉表示法,貌似很難判斷兩個字符串是否可以做這樣的變換。
對付複雜問題的方法是從簡單的特例來思考,從而找出規律。
先考察簡單情況:
字符串長度爲1:很明顯,兩個字符串必須完全相同纔可以。
字符串長度爲2:當s1="ab", s2只有"ab"或者"ba"纔可以。
對於任意長度的字符串,我們可以把字符串s1分爲a1,b1兩個部分,s2分爲a2,b2兩個部分,滿足((a1~a2) && (b1~b2))或者 ((a1~b2) && (a1~b2))

如此,我們找到了解決問題的思路。首先我們嘗試用遞歸來寫。


解法一(遞歸)

兩個字符串的相似的必備條件是含有相同的字符集。簡單的做法是把兩個字符串的字符排序後,然後比較是否相同。
加上這個檢查就可以大大的減少遞歸次數。
代碼如下:
public boolean isScramble(String s1, String s2) {
        int l1 = s1.length();
        int l2 = s2.length();
        if(l1!=l2){
            return false;
        }
        if(l1==0){
            return true;
        }
        
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        if(l1==1){
            return c1[0]==c2[0];
        }
        Arrays.sort(c1);
        Arrays.sort(c2);
        for(int i=0;i<l1;++i){
            if(c1[i]!=c2[i]){
                return false;
            }
        }
        
        boolean result = false;
        for(int i=1;i<l1 && !result;++i){
            String s11 = s1.substring(0,i);
            String s12 = s1.substring(i);
            String s21 = s2.substring(0,i);
            String s22 = s2.substring(i);
            result = isScramble(s11,s21) && isScramble(s12,s22);
            if(!result){
                String s31 = s2.substring(0,l1-i);
                String s32 = s2.substring(l1-i);
                result = isScramble(s11,s32) && isScramble(s12,s31);
            }
        }
        
        return result;
    }

解法二(動態規劃)
減少重複計算的方法就是動態規劃。動態規劃是一種神奇的算法技術,不親自去寫,是很難完全掌握動態規劃的。

這裏我使用了一個三維數組boolean result[len][len][len],其中第一維爲子串的長度,第二維爲s1的起始索引,第三維爲s2的起始索引。
result[k][i][j]表示s1[i...i+k]是否可以由s2[j...j+k]變化得來。

代碼如下,非常簡潔優美:

public class Solution {
    public boolean isScramble(String s1, String s2) {
        int len = s1.length();
        if(len!=s2.length()){
            return false;
        }
        if(len==0){
            return true;
        }
        
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        
        boolean[][][] result = new boolean[len][len][len];
        for(int i=0;i<len;++i){
            for(int j=0;j<len;++j){
                result[0][i][j] = (c1[i]==c2[j]);
            }
        }
        
        for(int k=2;k<=len;++k){
            for(int i=len-k;i>=0;--i){
              for(int j=len-k;j>=0;--j){
                  boolean r = false;
                  for(int m=1;m<k && !r;++m){
                      r = (result[m-1][i][j] && result[k-m-1][i+m][j+m]) || (result[m-1][i][j+k-m] && result[k-m-1][i+m][j]);
                  }
                  result[k-1][i][j] = r;
              }
            }
        }
        
        return result[len-1][0][0];
    }
}

posted @ 2013-05-22 22:25 小明 閱讀(1944) | 評論 (0) | 編輯 收藏

Problem

Given a collection of integers that might contain duplicates, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,2], a solution is:

[   [2],   [1],   [1,2,2],   [2,2],   [1,2],   [] ] 

分析:
因爲要求結果集是升序排列,所以首先我們要對數組進行排序。

子集的長度可以從0到整個數組的長度。長度爲n+1的子集可以由長度爲n的子集再加上在之後的一個元素組成。

這裏我使用了三個技巧
1。使用了一個index數組來記錄每個子集的最大索引,這樣添加新元素就很簡單。
2。使用了兩個變量start和end來記錄上一個長度的子集在結果中的起始和終止位置。
3。去重處理使用了一個last變量記錄前一次的值,它的初始值設爲S[0]-1,這樣就保證了和數組的任何一個元素不同。

代碼如下:
public class Solution {
    public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] S) {
        Arrays.sort(S);
        
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> indexs = new ArrayList<Integer>();
        result.add(new ArrayList<Integer>());
        indexs.add(-1);
        
        int slen = S.length;
        int start=0,end=0;
        for(int len=1;len<=slen;++len){
            int e = end;
            for(int i=start;i<=end;++i){
                ArrayList<Integer> ss = result.get(i);
                int index = indexs.get(i).intValue();
                int last = S[0]-1;
                for(int j = index+1;j<slen;++j){
                    int v = S[j];
                    if(v!=last){
                        ArrayList<Integer> newss = new ArrayList<Integer>(ss);
                        newss.add(v);
                        result.add(newss);
                        indexs.add(j);
                        ++e;
                        last = v;
                    }
                }
            }
            
            start = end+1;
            end = e;
        }
        return result;
    }
}

posted @ 2013-05-21 22:50 小明 閱讀(1423) | 評論 (0) | 編輯 收藏

問題格雷碼是一個二進制的編碼系統,相鄰的兩個數只有一位是不同的。
給定一個非負的整數n,代表了格雷碼的位的總數。輸出格雷碼的序列,這個序列必須以0開始。

比如,給定n=2,輸出[0,1,3,2],格雷碼是
0 = 00
1 = 01
3 = 11
2 = 10

注:格雷碼的序列並不是唯一,比如n=2時,[0,2,3,1]也滿足條件。


分析:
格雷碼的序列中應包含2^n個數。這個問題初看起來不容易,我們要想出一個生成方法。

對於n=2,序列是:
00,01,11,10
那對於n=3,如何利用n=2的序列呢?一個方法是,先在n=2的四個序列前加0(這其實是保持不變),然後再考慮把最高位變成1,只需要把方向反過來就可以了
000,001,011,010
100,101,111,110-> 110,111,101,100
把這兩行合起來就可以得到新的序列。

想通了,寫代碼就很容易了。

public class Solution {
    public ArrayList<Integer> grayCode(int n) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        result.add(0);
        if(n>0){
            result.add(1);
        }
        
        int mask = 1;
        for(int i=2;i<=n;++i){
            mask *= 2;
            for(int j=result.size()-1;j>=0;--j){
                int v = result.get(j).intValue();
                v |= mask;
                result.add(v);
            }
        }
        return result;
    }
}

posted @ 2013-05-20 21:09 小明 閱讀(1520) | 評論 (0) | 編輯 收藏

問題給定字符串s1,s2,s3,判斷s3是否可以由s1和s2交叉組成得到。

例如:

s1 = "aabcc",
s2 = "dbbca",

When s3 = "aadbbcbcac", return true.
When s3 = "aadbbbaccc", return false.


解法一:(遞歸)

一個簡單的想法,是遍歷s3的每個字符,這個字符必須等於s1和s2的某個字符。如果都不相等,則返回false
我們使用3個變量i,j,k分別記錄當前s1,s2,s3的字符位置。
如果s3[k] = s1[i], i向後移動一位。如果s3[k]=s2[j],j向後移動一位。
這個題目主要難在如果s1和s2的字符出現重複的時候,就有兩種情況,i,j都可以向後一位。
下面的算法在這種情況使用了遞歸,很簡單的做法。但是效率非常差,是指數複雜度的。

public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int l1 = s1.length();
        int l2 = s2.length();
        int l3 = s3.length();
        
        if(l1+l2!=l3){
            return false;
        }
        
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        char[] c3 = s3.toCharArray();
        
        int i=0,j=0;
        for(int k=0;k<l3;++k){
            char c = c3[k];
            boolean m1 = i<l1 && c==c1[i];
            boolean m2 = j<l2 && c==c2[j];
            if(!m1 && !m2){
                return false;
            }
            else if(m1 && m2){
                String news3 =  s3.substring(k+1);
                return isInterleave(s1.substring(i+1),s2.substring(j),news3)
                                || isInterleave(s1.substring(i),s2.substring(j+1),news3);
            }
            else if(m1){
                ++i;
            }
            else{
                ++j;
            }
        }
        
        return true;        
    }
}


解法二:(動態規劃)
爲了減少重複計算,就要使用動態規劃來記錄中間結果。

這裏我使用了一個二維數組result[i][j]來表示s1的前i個字符和s2的前j個字符是否能和s3的前i+j個字符匹配。

狀態轉移方程如下:
result[i,j] = (result[i-1,j] && s1[i] = s3[i+j])  || (result[i,j-1] && s2[j] = s3[i+j]);
其中0≤i≤len(s1) ,0≤j≤len(s2)

這樣算法複雜度就會下降到O(l1*l2)
public class Solution {
   
public boolean isInterleave(String s1, String s2, String s3) {
        int l1 = s1.length();
        int l2 = s2.length();
        int l3 = s3.length();
       
        if(l1+l2!=l3){
            return false;
        }
        
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        char[] c3 = s3.toCharArray();
        
        boolean[][] result = new boolean[l1+1][l2+1];
        result[0][0] = true;
        
        for(int i=0;i<l1;++i){
            if(c1[i]==c3[i]){
                result[i+1][0] = true;
            }
            else{
                break;
            }
        }
        
        for(int j=0;j<l2;++j){
            if(c2[j]==c3[j]){
                result[0][j+1] = true;
            }
            else{
                break;
            }
        }
        
        
        for(int i=1;i<=l1;++i){
            char ci = c1[i-1];
            for(int j=1;j<=l2;++j){
                char cj = c2[j-1];
                char ck = c3[i+j-1];
                   result[i][j] = (result[i][j-1] && cj==ck) || (result[i-1][j] && ci==ck);
            }
        }
        
        return result[l1][l2];
   }
}

posted @ 2013-05-10 20:47 小明 閱讀(1524) | 評論 (4) | 編輯 收藏

     摘要: 給定一個由n個整數組成的數組S,是否存在S中的三個數a,b,c使得 a+b+c=0?找出所有的不重複的和爲0的三元組。

注意:
1.三元組的整數按照升序排列 a2.給出的結果中不能含有相同的三元組  閱讀全文

posted @ 2013-05-01 23:13 小明 閱讀(1480) | 評論 (0) | 編輯 收藏

     摘要: 給定兩個字符串S和T,計算S的子序列爲T的個數。

這裏的字符串的子序列指的是刪除字符串的幾個字符(也可以不刪)而得到的新的字符串,但是不能改變字符的相對位置。

比如“ACE”是“ABCDE”的子序列,但是“AEC”就不是。

如果S=“rabbbit” T=“rabit”,有3種不同的子序列爲T的構成方法,那麼結果應該返回3。  閱讀全文

posted @ 2013-04-26 23:33 小明 閱讀(1290) | 評論 (1) | 編輯 收藏

問題給定一顆二叉樹:
class TreeLinkNode {
  TreeLinkNode left;
  TreeLinkNode right;
  TreeLinkNode next;
}
要求把所有節點的next節點設置成它右邊的節點,如果沒有右節點,設置成空。初始狀態,所有的next的指針均爲null.

要求:你只能使用常數的空間。

比如:
         1
       /  \
      2    3
     / \    \
    4   5    7
應該輸出:

1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \    \
    4-> 5 -> 7 -> NULL

分析:
題目不難,但是在面試時,在有限的時間內,沒有bug寫出,還是很考驗功力的。

解決這個問題的思路是逐層掃描,上一層設置好下一層的next關係,在處理空指針的時候要格外小心。
代碼如下,有註釋,應該很容易看懂:
使用了三個指針:
node:當前節點
firstChild:下一層的第一個非空子節點
lastChild:下一層的最後一個待處理(未設置next)的子節點

    public void connect(TreeLinkNode root) {
        TreeLinkNode node = root;
        TreeLinkNode firstChild = null;
        TreeLinkNode lastChild = null;
        
        while(node!=null){
            if(firstChild == null){ //記錄第一個非空子節點
                firstChild = node.left!=null?node.left:node.right;
            }
            //設置子節點的next關係,3種情況
            if(node.left!=null && node.right!=null){ 
                if(lastChild!=null){
                    lastChild.next = node.left;
                }
                node.left.next = node.right;
                lastChild = node.right;
            }
            else if(node.left!=null){
                if(lastChild!=null){
                    lastChild.next = node.left;
                }
                lastChild = node.left;
            }
            else if(node.right!=null){
                if(lastChild!=null){
                    lastChild.next = node.right;
                }
                lastChild = node.right;
            }
            //設置下一個節點,如果本層已經遍歷完畢,移到下一層的第一個子節點
            if(node.next!=null){
                node = node.next;
            }
            else{
                node = firstChild;
                firstChild = null;
                lastChild = null;
            }
        }
    }

posted @ 2013-04-26 11:23 小明 閱讀(1245) | 評論 (0) | 編輯 收藏

問題假設你有一個數組包含了每天的股票價格,它的第i個元素就是第i天的股票價格。 

設計一個算法尋找最大的收益。你可以最多進行兩次交易。
注意:你不能同時進行多次交易,也就是說你買股票之前,必須賣掉手中股票。


分析:
這道題相比之前的兩道題,難度提高了不少。

因爲限制了只能交易兩次,所以我們可以把n天分爲兩段,分別計算這兩段的最大收益,就可以得到一個最大收益。窮舉所有這樣的分法,就可以得到全局的最大收益。

爲了提高效率,這裏使用動態規劃,即把中間狀態記錄下來。使用了兩個數組profits,nprofits分別記錄從0..i和i..n的最大收益。

代碼如下:

public int maxProfit(int[] prices) {
        int days = prices.length;
        if(days<2){
            return 0;
        }
        int[] profits = new int[days];
        int min = prices[0];
        int max = min;
        for(int i=1;i<days;++i){
            int p = prices[i];
            if(min>p){
                max = min = p;
            }
            else if(max<p){
                max = p;
            }
            int profit = max - min;
            profits[i] = (profits[i-1]>profit)?profits[i-1]:profit;
        }
        
        int[] nprofits = new int[days];
        nprofits[days-1] = 0;
        max = min = prices[days-1];
        for(int i=days-2;i>=0;--i){
            int p = prices[i];
            if(min>p){
                min =p;
            }
            else if(max<p){
                max = min = p;
            }
            int profit = max - min;
            nprofits[i] = (nprofits[i+1]>profit)?nprofits[i+1]:profit;
        }
        
        int maxprofit = 0;
        
        for(int i=0;i<days;++i){
            int profit = profits[i]+nprofits[i];
            if(maxprofit<profit){
                maxprofit = profit;
            }
        }
        
        return maxprofit;        
    }

posted @ 2013-04-25 22:22 小明 閱讀(1286) | 評論 (0) | 編輯 收藏

問題假設你有一個數組包含了每天的股票價格,它的第i個元素就是第i天的股票價格。

設計一個算法尋找最大的收益。你可以進行任意多次交易。但是,你不能同時進行多次交易,也就是說你買股票之前,必須賣掉手中股票。

分析:爲了得到最大收益,必須在所有上升的曲線段的開始點買入,在最高點賣出。而在下降階段不出手。



實現代碼如下:
public class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        if(len<2){
            return 0;
        }
        
        int min=0;
        int result = 0;
        boolean inBuy = false;
        for(int i=0;i<len-1;++i){
            int p = prices[i];
            int q = prices[i+1];
            if(!inBuy){
                if(q>p){
                    inBuy = true;
                    min=p ;
                }
            }
            else{
                if(q<p){
                    result += (p-min);
                    inBuy = false;
                }
            }
        }
        if(inBuy){
            result += ((prices[len-1])-min);
        }
        return result;
    }
}

posted @ 2013-04-19 21:50 小明 閱讀(1344) | 評論 (0) | 編輯 收藏

     摘要: 假設你有一個數組包含了每天的股票價格,它的第i個元素就是第i天的股票價格。

你只能進行一次交易(一次買進和一次賣出),設計一個算法求出最大的收益。  閱讀全文

posted @ 2013-04-19 15:03 小明 閱讀(1198) | 評論 (0) | 編輯 收藏

     摘要: 給定一個二叉樹,尋找最大的路徑和.
路徑可以從任意節點開始到任意節點結束。(也可以是單個節點)

比如:對於二叉樹
1
/ \
2 3
和最大的路徑是2->1->3,結果爲6
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/  閱讀全文

posted @ 2013-04-18 21:31 小明 閱讀(1233) | 評論 (0) | 編輯 收藏

     摘要: 給定兩個單詞(一個開始,一個結束)和一個字典,找出所有的最短的從開始單詞到結束單詞的變換序列的序列(可能不止一個),並滿足:

1.每次只能變換一個字母
2.所有的中間單詞必須存在於字典中

比如:
輸入:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

那麼最短的變化序列有兩個
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]。
注意:
1. 所有單詞的長度都是相同的
2. 所有單詞都只含有小寫的字母。  閱讀全文

posted @ 2013-04-18 17:32 小明 閱讀(1040) | 評論 (0) | 編輯 收藏

     摘要: 給定兩個排序好的數組A和B,把B合併到A並保持排序。

public class Solution {
public void merge(int A[], int m, int B[], int n) {
//write your code here }
}

注意:
假定A有足夠的額外的容量儲存B的內容,m和n分別爲A和B的初始化元素的個數。要求算法複雜度在O(m+n)。  閱讀全文

posted @ 2013-04-18 13:44 小明 閱讀(990) | 評論 (0) | 編輯 收藏

     摘要: 給定兩個單詞(一個開始,一個結束)和一個字典,找出最短的從開始單詞到結束單詞的變換序列的長度,並滿足:

1.每次只能變換一個字母
2.所有的中間單詞必須存在於字典中

比如:
輸入:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

那麼最短的變化序列是"hit" -> "hot" -> "dot" -> "dog" -> "cog",所以返回長度是5。
注意:
1. 如果找不到這樣的序列,返回0
2. 所有單詞的長度都是相同的
3. 所有單詞都只含有小寫的字母。  閱讀全文

posted @ 2013-04-18 12:46 小明 閱讀(939) | 評論 (0) | 編輯 收藏

     摘要: 給定一個二叉樹,每個節點的值是一個數字(0-9),每個從根節點到葉節點均能組成一個數字。
比如如果從根節點到葉節點的路徑是1-2-3,那麼這代表了123這個數字。
求出所有這樣從根節點到葉節點的數字之和。

比如,對於二叉樹
1
/ \
2 3

一共有兩條路徑1->2和1->3,那麼求和的結果就是12+13=25
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public int sumNumbers(TreeNode root) {
//write c  閱讀全文

posted @ 2013-04-16 11:37 小明 閱讀(1226) | 評論 (1) | 編輯 收藏

     摘要: 給定一個2D的棋盤,含有‘X'和’O',找到所有被‘X'包圍的’O',然後把該區域的‘O’都變成'X'。

例子-輸入:
X X X X
X O O X
X X O X
X O X X

應該輸出:

X X X X
X X X X
X X X X
X O X X

public void solve(char[][] board) {
}  閱讀全文

posted @ 2013-04-15 18:17 小明 閱讀(1148) | 評論 (2) | 編輯 收藏

     摘要: 給定一個字符串s,切割字符串使得每個子串都是迴文的。(比如aba,對稱)
要求返回所有可能的分割。

比如,對於字符串s="aab",
返回:

[
["aa","b"],
["a","a","b"]
]
  閱讀全文

posted @ 2013-04-15 13:52 小明 閱讀(1112) | 評論 (0) | 編輯 收藏

+1

     摘要: 給定一個有由數字構成的數組表示的數,求該數加1的結果。
public class Solution {
public int[] plusOne(int[] digits) {
}
}  閱讀全文

posted @ 2013-04-15 11:22 小明 閱讀(1042) | 評論 (3) | 編輯 收藏

     摘要: 實現 int sqrt(int x);
計算和返回x的平方根。  閱讀全文

posted @ 2013-04-15 10:19 小明 閱讀(1083) | 評論 (0) | 編輯 收藏

     摘要: 給定一個未排序的整數數組,求最長的連續序列的長度。要求算法的時間複雜度在O(n)
比如對於數組[100, 4, 200, 1, 3, 2],其中最長序列爲[1,2,3,4],所以應該返回4

public class Solution {
public int longestConsecutive(int[] num) {
//write your code here
}
}  閱讀全文

posted @ 2013-04-12 15:58 小明 閱讀(1238) | 評論 (7) | 編輯 收藏

     摘要: 給定一個字符串s,分割s使得每個子串都是迴文的(即倒過來和原字符串是一樣的,如aba)
求最少的分割次數。  閱讀全文

posted @ 2013-04-11 11:24 小明 閱讀(1449) | 評論 (2) | 編輯 收藏

問題描述:
Problem Statement
THIS PROBLEM WAS TAKEN FROM THE SEMIFINALS OF THE TOPCODER INVITATIONAL
TOURNAMENT
DEFINITION
Class Name: MatchMaker
Method Name: getBestMatches
Paramaters: String[], String, int
Returns: String[]
Method signature (be sure your method is public):  String[]
getBestMatches(String[] members, String currentUser, int sf);
PROBLEM STATEMENT
A new online match making company needs some software to help find the "perfect
couples".  People who sign up answer a series of multiple-choice questions.
Then, when a member makes a "Get Best Mates" request, the software returns a
list of users whose gender matches the requested gender and whose answers to
the questions were equal to or greater than a similarity factor when compared
to the user's answers.
Implement a class MatchMaker, which contains a method getBestMatches.  The
method takes as parameters a String[] members, String currentUser, and an int
sf:
- members contains information about all the members.  Elements of members are
of the form "NAME G D X X X X X X X X X X" 
   * NAME represents the member's name
   * G represents the gender of the current user. 
   * D represents the requested gender of the potential mate. 
* Each X indicates the member's answer to one of the multiple-choice
questions.  The first X is the answer to the first question, the second is the
answer to the second question, et cetera. 
- currentUser is the name of the user who made the "Get Best Mates" request.  
- sf is an integer representing the similarity factor.
The method returns a String[] consisting of members' names who have at least sf
identical answers to currentUser and are of the requested gender.  The names
should be returned in order from most identical answers to least.  If two
members have the same number of identical answers as the currentUser, the names
should be returned in the same relative order they were inputted.
TopCoder will ensure the validity of the inputs.  Inputs are valid if all of
the following criteria are met:
- members will have between 1 and 50 elements, inclusive.
- Each element of members will have a length between 7 and 44, inclusive.
- NAME will have a length between 1 and 20, inclusive, and only contain
uppercase letters A-Z.
- G can be either an uppercase M or an uppercase F.
- D can be either an uppercase M or an uppercase F.
- Each X is a capital letter (A-D).
- The number of Xs in each element of the members is equal.  The number of Xs
will be between 1 and 10, inclusive. 
- No two elements will have the same NAME.
- Names are case sensitive.
- currentUser consists of between 1 and 20, inclusive, uppercase letters, A-Z,
and must be a member.
- sf is an int between 1 and 10, inclusive.
- sf must be less than or equal to the number of answers (Xs) of the members.
NOTES
The currentUser should not be included in the returned list of potential mates.
EXAMPLES
For the following examples, assume members =
{"BETTY F M A A C C",
 "TOM M F A D C A",
 "SUE F M D D D D",
 "ELLEN F M A A C A",
 "JOE M F A A C A",
 "ED M F A D D A",
 "SALLY F M C D A B",
 "MARGE F M A A C C"}
If currentUser="BETTY" and sf=2, BETTY and TOM have two identical answers and
BETTY and JOE have three identical answers, so the method should return
{"JOE","TOM"}.
If currentUser="JOE" and sf=1, the method should return
{"ELLEN","BETTY","MARGE"}.
If currentUser="MARGE" and sf=4, the method should return [].
Definition
Class:
MatchMaker
Method:
getBestMatches
Parameters:
String[], String, int
Returns:
String[]
Method signature:
String[] getBestMatches(String[] param0, String param1, int param2)
(be sure your method is public)


================================================================我的代碼=============

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MatchMaker {
    enum GENDER{MALE,FEMALE};
    
    //"NAME G D X X X X X X X X X X" 
    private static class Member{
        String name;
        GENDER gender;
        GENDER mate;
        String[] answers;
        int index;
        int matched = 0;
    }
    
    String[] getBestMatches(String[] members, String currentUser, int sf){
        List<Member> allMembers = new ArrayList<Member>();
        Member cu = null;
        for(int i=0;i<members.length;++i){
            String m = members[i];
            String[] c = m.split(" ");
            Member mem = new Member();
            mem.name= c[0];
            mem.gender = c[1].equals("M")?GENDER.MALE:GENDER.FEMALE;
            mem.mate = c[2].equals("M")?GENDER.MALE:GENDER.FEMALE;
            mem.index = i;
            mem.matched = 0;
            String[] answers = mem.answers = new String[c.length-3];
            for(int j=3;j<c.length;++j){
                answers[j-3] = c[j];
            }
            allMembers.add(mem);
            if(c[0].equals(currentUser)){
                cu = mem;
            }
        }
        List<Member> matched = new ArrayList<Member>();
        if(cu!=null){
            for(Member mem:allMembers){
                if(mem!=cu && mem.gender==cu.mate){
                    for(int i=0;i<mem.answers.length;++i){
                        if(mem.answers[i].equals(cu.answers[i])){
                            ++mem.matched;
                        }
                    }
                    if(mem.matched>=sf){
                        matched.add(mem);
                    }
                }
            }
            
            Collections.sort(matched, new Comparator<Member>(){
                public int compare(Member ma, Member mb) {
                    if(ma.matched!=mb.matched){
                        return mb.matched - ma.matched;
                    }
                    return ma.index-mb.index;
                }
            });
            
            String[] result = new String[matched.size()];
            for(int i=0;i<result.length;++i){
                result[i] = matched.get(i).name;
            }
            return result;
        }
        return new String[0];
    }
}


posted @ 2013-04-02 14:04 小明 閱讀(62) | 評論 (0) | 編輯 收藏

以下是我在上一家公司出的java筆試題,有些難度,感興趣的同學可以做做看。

---Question---

1.What is the output of the following program? 

public class Foo {

       public static void main(String[] args){

              Map<byte[], String> m = new HashMap<byte[], String>();

              byte[] key = "abcd".getBytes();

              m.put(key, "abcd");

              System.out.println(m.containsKey(key));

              System.out.println(m.containsKey("abcd"));

              System.out.println(m.containsKey("abcd".getBytes()));

       }

}

a) true,true,false b)true,false,false c)true,true,true d) false,false,false e)Program throws an exception

 

2. What is the proper string filled in the following program?

Public class Foo {

       public static void main(String[] args) {

              String s=”1\\2\\3\\4”;

              //split the string with “\”

              String []result = s.split(“____”);

              for(String r:result){

                     System.out.println(r);

              }

       }

}

a) \ b) \\ c) \\\ d)\\\\ e)\\\\\

 

3. What is the output of the following program? 

public class Foo {

       public static void main(String[] args) {

              char[] c = new char[] { '1' };

              String s = new String(c);

              System.out.println("abcd" + c);

              System.out.println("abcd" + s);

       }

}

a) Compile error b)abcd1,abcd1 c) abcd49,abcd1 d) Program throws an exception e)none of above

 

4. Which class is threading safe which one object can be used between multi-threads without extra synchronized? 

a) Vector b) HashMap c) ArrayList d)StringBuilder e)HashSet

 

5. What is the output of the following program? 

public class Foo {

       public static void main(String[] args) throws IOException {

              ByteArrayOutputStream baos = new ByteArrayOutputStream();

              byte[] b = new byte[]{(byte)0x0,(byte)0x1,(byte)0x2};

              baos.write(b);

              baos.write(0x0102);

              byte[] result = baos.toByteArray();

              ByteArrayInputStream bais = new ByteArrayInputStream(result);

              System.out.println(bais.available());

       }

}

a) 0 b) 1 c)4 d) 5 e) Program throws an exception

 

6. What is return value of function “calc”?

public class Foo {

       public static int calc() throws IOException{

              int ret = 0;

              try{

                     ++ret;

                     throw new IOException("try");

              }

              catch(IOException ioe){

                     --ret;

                     return ret;

              }

              finally{

                     ++ret;

                     return ret;

              }

       }

}

a) 0 b) 1 c)2 d)3 e) throws an exception

 

7. What is the output of the following program?

public class Foo {

       public static class Value {

              private int value;

              public int get(){

                     return value;

              }

              public void set(int v){

                     value = v;

              }

       }

       public static class Values implements Iterable<Value>{

              public Values(int capacity){

                     this.capacity = capacity;

              }

             

              int count =1 ;

              int capacity;

              Value v = new Value();

              public Iterator<Value> iterator() {

                     return new Iterator<Value>(){

                            public boolean hasNext() {

                                   return count<=capacity;

                            }

 

                            public Value next() {

                                   v.set(count++);

                                   return v;

                            }

 

                            public void remove() {

                                   throw new UnsupportedOperationException();

                            }

                     };

              }

       }

       public static void main(String[] args) {

              Values vs = new Values(10);

              Value result = null;

              for(Value v:vs){

                     if(result ==  null){

                            result = v;

                     }

                     else{

                            result.set(result.get()+v.get());

                     }

              }

              System.out.println(result.get());

       }

}

a)       20 b)40 c)45 d)55 e)throws NullpointerException

 

8. If add keyword “final” before a class member function, it means:

a) The method can’t access the non-final member variable.

b) The method can’t modify the member variable.

c) The method can’t be override by subclass.

d) The method is a thread-safe function.

e) The method can’t be accessed by other non-final function.

 

9. About Java memory and garbage collector, which statement is correct?

a) Moving variable from locale to class will make GC more effectively.

b) When Full GC is executing, all the user threads will be paused.

c) If object A contains a reference of object B and object B contains a reference of object A, the two objects can’t be reclaimed by GC.

d) When a thread exits, all objects which created by that thread will be reclaimed

e) It is recommended that calling “System.gc()” to control the memory usage.

 

10. About Java classpath and classloader, which statement is NOT correct?

a) User can specify the classpath by using the option “-cp” in Java command line.

b) If user doesn’t specify classpath, the JVM search the class from the current folder by default.

c) A JVM can load two different versions of a library.

d) To define customized class loader, it is possible to load class from internet at runtime.

 

 

11. Which data structure has best performance when remove an element from it?

a) Vector b)ArrayList c)LinkedList d)HashMap e)HashSet

 

12. Which is the correct way to convert bytes from charset “gb2312” to “utf-8”?

byte[] src , dst;

a) dst = new String(src,”utf-8”).getBytes(“gb2312”);

b) dst = new String(src,”gb2312”).getBytes(“utf-8”);

c) dst = new String(src,”utf-16”).getBytes();

d) dst = new String(src).getBytes();

e) None of above.

 

posted @ 2012-11-07 09:46 小明 閱讀(1711) | 評論 (3) | 編輯 收藏

     摘要: 準備工作:1. 下載Snappy庫Download source code from: http://code.google.com/p/snappy編譯並安裝./configure & make & sudo make install2. 編譯leveldb自帶的db_benchmake db_bench注意:在ubuntu 11.04上編譯會出錯,修改makefile:$(CX...  閱讀全文

posted @ 2012-03-22 17:32 小明 閱讀(1842) | 評論 (0) | 編輯 收藏

leveldb讀數據

先看看ReadOptions有哪些參數可以指定:
// Options that control read operations
struct ReadOptions {
  
// 是否檢查checksum
  
// Default: false
  bool verify_checksums;

  
// 是否將此次結果放入cache
  
// Default: true
  bool fill_cache;

  
//是否指定snapshot,否則讀取當前版本
  
// Default: NULL
  const Snapshot* snapshot;

  ReadOptions()
      : verify_checksums(
false),
        fill_cache(
true),
        snapshot(NULL) {
  }
};

下面看看讀取的詳細過程:
查詢memtable=>查詢previous memtable(imm_)=>查詢文件(緩衝)

Status DBImpl::Get(const ReadOptions& options,
                   
const Slice& key,
                   std::
string* value) {
  Status s;
  MutexLock l(
&mutex_);
  SequenceNumber snapshot;
  
//設置snapshot
  if (options.snapshot != NULL) {
    snapshot 
= reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;
  } 
else {
    snapshot 
= versions_->LastSequence();
  }

  MemTable
* mem = mem_;
  MemTable
* imm = imm_;
  Version
* current = versions_->current();
  mem
->Ref();
  
if (imm != NULL) imm->Ref();
  current
->Ref();

  
bool have_stat_update = false;
  Version::GetStats stats;

  
// Unlock while reading from files and memtables
  {
    mutex_.Unlock();
    LookupKey lkey(key, snapshot);
    
//先查詢memtable
    if (mem->Get(lkey, value, &s)) {
      
// Done
    } else if (imm != NULL && imm->Get(lkey, value, &s)) { //然後查詢previous memtable:imm_
      
// Done
    } else {
      
//從文件中讀取
      s = current->Get(options, lkey, value, &stats);
      have_stat_update 
= true;
    }
    mutex_.Lock();
  }

  
//是否有文件需要被compaction,參見allowed_seek
  if (have_stat_update && current->UpdateStats(stats)) {
    MaybeScheduleCompaction();
  }
  mem
->Unref();
  
if (imm != NULL) imm->Unref();
  current
->Unref();
  
return s;
}


重點來看看從version中讀取:
Status Version::Get(const ReadOptions& options,
                    
const LookupKey& k,
                    std::
string* value,
                    GetStats
* stats) {
  Slice ikey 
= k.internal_key();
  Slice user_key 
= k.user_key();
  
const Comparator* ucmp = vset_->icmp_.user_comparator();
  Status s;

  stats
->seek_file = NULL;
  stats
->seek_file_level = -1;
  FileMetaData
* last_file_read = NULL;
  
int last_file_read_level = -1;

  
//從level0向高層查找,如果再低級level中查到,則不再查詢
  std::vector<FileMetaData*> tmp;
  FileMetaData
* tmp2;
  
for (int level = 0; level < config::kNumLevels; level++) {
    size_t num_files 
= files_[level].size();
    
//本層文件數爲空,則返回
    if (num_files == 0continue;

    
// Get the list of files to search in this level
    FileMetaData* const* files = &files_[level][0];
    
if (level == 0) {
      
//level0特殊處理,因爲key是重疊,所有符合條件的文件必須被查找
      tmp.reserve(num_files);
      
for (uint32_t i = 0; i < num_files; i++) {
        FileMetaData
* f = files[i];
        
if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&
            ucmp
->Compare(user_key, f->largest.user_key()) <= 0) {
          tmp.push_back(f);
        }
      }
      
if (tmp.empty()) continue;

      std::sort(tmp.begin(), tmp.end(), NewestFirst);
      files 
= &tmp[0];
      num_files 
= tmp.size();
    } 
else {
      
// 二分法查找,某個key只可能屬於一個文件
      uint32_t index = FindFile(vset_->icmp_, files_[level], ikey);
      
//沒有查到
      if (index >= num_files) {
        files 
= NULL;
        num_files 
= 0;
      } 
else {
        tmp2 
= files[index];
        
if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) {
          
// All of "tmp2" is past any data for user_key
          files = NULL;
          num_files 
= 0;
        } 
else {
          files 
= &tmp2;
          num_files 
= 1;
        }
      }
    }

    
for (uint32_t i = 0; i < num_files; ++i) { //遍歷本層符合條件的文件
      if (last_file_read != NULL && stats->seek_file == NULL) {
        
//seek_file只記錄第一個
        stats->seek_file = last_file_read;
        stats
->seek_file_level = last_file_read_level;
      }

      FileMetaData
* f = files[i];
      last_file_read 
= f;
      last_file_read_level 
= level;
      
      
//從table cache中讀取
      Iterator* iter = vset_->table_cache_->NewIterator(
          options,
          f
->number,
          f
->file_size);
      iter
->Seek(ikey);
      
const bool done = GetValue(ucmp, iter, user_key, value, &s);
      
if (!iter->status().ok()) { //查找到
        s = iter->status();
        delete iter;
        
return s;
      } 
else {
        delete iter;
        
if (done) {
          
return s;
        }
      }
    }
  }

  
return Status::NotFound(Slice());  // Use an empty error message for speed
}

繼續跟蹤:TableCache

Iterator* TableCache::NewIterator(const ReadOptions& options,
                                  uint64_t file_number,
                                  uint64_t file_size,
                                  Table
** tableptr) {
  
if (tableptr != NULL) {
    
*tableptr = NULL;
  }

  
char buf[sizeof(file_number)];
  EncodeFixed64(buf, file_number);
  Slice key(buf, 
sizeof(buf));

  
//從LRU cache中查找
  Cache::Handle* handle = cache_->Lookup(key);
  
if (handle == NULL) { 
    
/加載文件
    std::
string fname = TableFileName(dbname_, file_number);
    RandomAccessFile
* file = NULL;
    Table
* table = NULL;
    Status s 
= env_->NewRandomAccessFile(fname, &file);
    
if (s.ok()) {
      s 
= Table::Open(*options_, file, file_size, &table);
    }

    
if (!s.ok()) {
      assert(table 
== NULL);
      delete file;
      
// We do not cache error results so that if the error is transient,
      
// or somebody repairs the file, we recover automatically.
      return NewErrorIterator(s);
    }

    
//插入Cache
    TableAndFile* tf = new TableAndFile;
    tf
->file = file;
    tf
->table = table;
    handle 
= cache_->Insert(key, tf, 1&DeleteEntry);
  }

  Table
* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
  
//從Table對象中生成iterator
  Iterator* result = table->NewIterator(options);
  result
->RegisterCleanup(&UnrefEntry, cache_, handle);
  
if (tableptr != NULL) {
    
*tableptr = table;
  }
  
return result;
}


posted @ 2012-03-21 17:30 小明 閱讀(1352) | 評論 (0) | 編輯 收藏

總體來說,leveldb的寫操作有兩個步驟,首先是針對log的append操作,然後是對memtable的插入操作。

影響寫性能的因素有:
1. write_buffer_size
2. kL0_SlowdownWritesTrigger and kL0_StopWritesTrigger.提高這兩個值,能夠增加寫的性能,但是降低讀的性能

看看WriteOptions有哪些參數可以指定
struct WriteOptions {
  
//設置sync=true,leveldb會調用fsync(),這會降低插入性能
  
//同時會增加數據的安全性 
  
//Default: false
  bool sync;

  WriteOptions()
      : sync(
false) {
  }
};


首先把Key,value轉成WriteBatch
Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) {
  WriteBatch batch;
  batch.Put(key, value);
  
return Write(opt, &batch);
}

接下來就是真正的插入了
這裏使用了兩把鎖,主要是想提高併發能力,減少上鎖的時間。
首先是檢查是否可寫,然後append log,最後是插入memtable
<db/dbimpl.cc>

Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
  Status status;
  
//加鎖
  MutexLock l(&mutex_);
  LoggerId self;
  
//拿到寫log的權利
  AcquireLoggingResponsibility(&self);
  
//檢查是否可寫
  status = MakeRoomForWrite(false);  // May temporarily release lock and wait
  uint64_t last_sequence = versions_->LastSequence();
  
if (status.ok()) {
    WriteBatchInternal::SetSequence(updates, last_sequence 
+ 1);
    last_sequence 
+= WriteBatchInternal::Count(updates);

    
// Add to log and apply to memtable.  We can release the lock during
    
// this phase since the "logger_" flag protects against concurrent
    
// loggers and concurrent writes into mem_.
    {
      assert(logger_ 
== &self);
      mutex_.Unlock();
      
//IO操作:寫入LOG
      status = log_->AddRecord(WriteBatchInternal::Contents(updates));
      
if (status.ok() && options.sync) {
        status 
= logfile_->Sync();
      }
      
//插入memtable
      if (status.ok()) {
        status 
= WriteBatchInternal::InsertInto(updates, mem_);
      }
      mutex_.Lock();
      assert(logger_ 
== &self);
    }
    
//設置新的seqence number
    versions_->SetLastSequence(last_sequence);
  }
  
//釋放寫LOG鎖
  ReleaseLoggingResponsibility(&self);
  
return status;
}

寫流量控制:
<db/dbimpl.cc>
Status DBImpl::MakeRoomForWrite(bool force) {
  mutex_.AssertHeld();
  assert(logger_ 
!= NULL);
  
bool allow_delay = !force;
  Status s;
  
while (true) {
    
if (!bg_error_.ok()) {
      
// Yield previous error
      s = bg_error_;
      
break;
    } 
else if ( 
        allow_delay 
&&
        versions_
->NumLevelFiles(0>= config::kL0_SlowdownWritesTrigger) {
      mutex_.Unlock();
      
//如果level0的文件大於kL0_SlowdownWritesTrigger閾值,則sleep 1s,這樣給compaction更多的CPU
      env_->SleepForMicroseconds(1000);
      allow_delay 
= false;  // Do not delay a single write more than once
      mutex_.Lock();
    } 
else if (!force &&
               (mem_
->ApproximateMemoryUsage() <= options_.write_buffer_size)) {
      
//可寫
      break;
    } 
else if (imm_ != NULL) {
      
// imm_:之前的memtable 沒有被compaction,需要等待
      bg_cv_.Wait();
    } 
else if (versions_->NumLevelFiles(0>= config::kL0_StopWritesTrigger) {
      
// level0文件個數大於kL0_StopWritesTrigger,需要等待
      Log(options_.info_log, "waiting\n");
      bg_cv_.Wait();
    } 
else {
      
//生成新的額memtable和logfile,把當前memtable傳給imm_
      assert(versions_->PrevLogNumber() == 0);
      uint64_t new_log_number 
= versions_->NewFileNumber();
      WritableFile
* lfile = NULL;
      s 
= env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);
      
if (!s.ok()) {
        
break;
      }
      delete log_;
      delete logfile_;
      logfile_ 
= lfile;
      logfile_number_ 
= new_log_number;
      log_ 
= new log::Writer(lfile);
      imm_ 
= mem_;
      has_imm_.Release_Store(imm_);
      mem_ 
= new MemTable(internal_comparator_);
      mem_
->Ref();
      force 
= false;   // Do not force another compaction if have room
      // 發起compaction,dump imm_
      MaybeScheduleCompaction();
    }
  }
  
return s;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章