关于数组,字符串的几道面试题和算法题

转载请标明出处,原文地址: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  ==   0 continue ;

    
//  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;
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值