小程序员的趣味题(一)
1.在排序数组中,找出某整数出现的次数
问题定义:给定一个整数数组arr,数组中元素的个数是n,数组arr已经排好序,要在arr中找到某个某个整数x出现的次数,比如arr[] = {1,2,2,3,5,10},找到2的出现次数就是2。
问题分析:相必看到有序数组的字样,想到利用二分应该是很顺利成章的事了。我们可以利用二分搜索求出x在arr中出现的第一个位置lo和最后一个位置hi,然后计算hi-lo+1的值就是x在arr出现的次数了,当然也有可能x并没有在arr中出现过,这时hi和lo都等于-1。时间复杂度是两个二分的复杂度:2*O(log n)。看看代码是怎么实现的吧!!
相关代码:
#include <iostream> 2 3 using namespace std; 4 5 //求上届,返回值是-1表示在arr中没找到x 6 int upper_bound(int *arr,int n,int x) 7 { 8 bool flag = false;//用于判断x是否存在于arr中 9 int lo,hi,mid; 10 lo = 0 ; hi = n-1; 11 while(lo < hi) 12 { 13 mid = (lo + hi + 1) / 2;//求上界要保证能取到最大值 14 if(arr[mid] <= x) 15 { 16 if(arr[mid] == x)//x在arr中出现过 17 flag = true; 18 lo = mid; 19 } 20 else 21 { 22 hi = mid - 1; 23 } 24 } 25 if(flag) 26 return lo; 27 return -1; 28 } 29 30 //求下界,返回值是-1表示在arr中没找到x 31 int lower_bound(int *arr,int n,int x) 32 { 33 bool flag = false;//用于判断x是否存在于arr中 34 int lo,hi,mid; 35 lo = 0 ; hi = n-1; 36 while(lo < hi) 37 { 38 mid = (lo + hi) / 2; 39 if(arr[mid] >= x) 40 { 41 if(arr[mid] == x) 42 flag = true; 43 hi = mid; 44 } 45 else 46 { 47 lo = mid + 1; 48 } 49 } 50 if(flag) 51 return lo; 52 return -1; 53 } 54 //计算x在arr中的出现次数 55 void count(int *arr,int n,int x) 56 { 57 int hi = upper_bound(arr,n,x); 58 int lo = lower_bound(arr,n,x); 59 if(hi != -1) 60 { 61 cout<<x<<" 在arr中出现了 "<<hi - lo +1 <<" 次 !!"<<endl; 62 } 63 else 64 { 65 cout<<x<<" 在arr中出现了 0 次 !!"<<endl; 66 } 67 } 68 int main() 69 { 70 int arr[] = {1,2,2,3,5,6}; 71 count(arr,6,4); 72 count(arr,6,2); 73 return 0; 74 }
1 #include <iostream> 2 3 using namespace std; 4 5 //求上届,返回值是-1表示在arr中没找到x 6 int upper_bound(int *arr,int n,int x) 7 { 8 bool flag = false;//用于判断x是否存在于arr中 9 int lo,hi,mid; 10 lo = 0 ; hi = n-1; 11 while(lo < hi) 12 { 13 mid = (lo + hi + 1) / 2;//求上界要保证能取到最大值 14 if(arr[mid] <= x) 15 { 16 if(arr[mid] == x)//x在arr中出现过 17 flag = true; 18 lo = mid; 19 } 20 else 21 { 22 hi = mid - 1; 23 } 24 } 25 if(flag) 26 return lo; 27 return -1; 28 } 29 30 //求下界,返回值是-1表示在arr中没找到x 31 int lower_bound(int *arr,int n,int x) 32 { 33 bool flag = false;//用于判断x是否存在于arr中 34 int lo,hi,mid; 35 lo = 0 ; hi = n-1; 36 while(lo < hi) 37 { 38 mid = (lo + hi) / 2; 39 if(arr[mid] >= x) 40 { 41 if(arr[mid] == x) 42 flag = true; 43 hi = mid; 44 } 45 else 46 { 47 lo = mid + 1; 48 } 49 } 50 if(flag) 51 return lo; 52 return -1; 53 } 54 //计算x在arr中的出现次数 55 void count(int *arr,int n,int x) 56 { 57 int hi = upper_bound(arr,n,x); 58 int lo = lower_bound(arr,n,x); 59 if(hi != -1) 60 { 61 cout<<x<<" 在arr中出现了 "<<hi - lo +1 <<" 次 !!"<<endl; 62 } 63 else 64 { 65 cout<<x<<" 在arr中出现了 0 次 !!"<<endl; 66 } 67 } 68 int main() 69 { 70 int arr[] = {1,2,2,3,5,6}; 71 count(arr,6,4); 72 count(arr,6,2); 73 return 0; 74 }
小结:有人也提到了这样解法,即:先利用二分找到x在arr中出现的第一个位置lo,然后从lo下一位置开始向后直到下标达到n-1或者找到不等于x的情况结束,每前进一步,x出现的次数加 1,这样也能求出x在arr中出现的次数,但是不要忘了这种情况,即arr = {1,1,1,1,1,1},然后x = 1,那么这时利用这种方法的时间复杂度就是O(n+log n) 了。也就是说在最坏的情况下,时间复杂度太高。
2.去掉字符串中多余的空格
问题描述:给定一个字符串数组,要求去掉首部和尾部的所有空格,字符串中间的空格出现1次以上只保留一个空格,比如str[] = “ Wo shi Vincent ”,经过处理后应为“Wo shi Vincent”。
问题分析:这是一个简单的字符串处理的问题,显然开头、结尾、和中间的空格处理方式是不一样的,我的思路是遍历一遍字符数组,首先处理开头的空格,方法是:全部删除;然后是字符串中间的空格,方法是:遇到空格,值保留第一个空格;遍历结束后,新串的最后一个字符是空格,就代表是原字符串尾部有空格存在,所以要去掉。如此一来就完事了,值需要遍历一遍,时间复杂度是O(n)。
相关代码:
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 void remove_extra_space(char *str) 7 { 8 int Start = 0 , End = strlen(str); 9 int NewStart = 0; 10 while(Start < End) 11 { 12 //去掉开头的空格 13 if(Start == 0) 14 { 15 while(Start < End && str[Start] == ' ') 16 { 17 Start++; 18 } 19 } 20 //处理中间的空格,多个空格值保存一个 21 if(str[Start] == ' ') 22 { 23 str[NewStart++] = str[Start++]; 24 while(Start < End && str[Start] == ' ') 25 { 26 Start++; 27 } 28 } 29 //复制非空格的字符 30 else 31 { 32 str[NewStart++] = str[Start++]; 33 } 34 } 35 //去掉末尾的空格 36 if(str[NewStart-1] == ' ') 37 { 38 str[NewStart-1] = '\0'; 39 } 40 } 41 42 int main() 43 { 44 char str[] = " wo shi Vincent "; 45 remove_extra_space(str); 46 cout<<str<<"@@@@@@"<<endl; 47 return 0; 48 }
1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 void remove_extra_space(char *str) 7 { 8 int Start = 0 , End = strlen(str); 9 int NewStart = 0; 10 while(Start < End) 11 { 12 //去掉开头的空格 13 if(Start == 0) 14 { 15 while(Start < End && str[Start] == ' ') 16 { 17 Start++; 18 } 19 } 20 //处理中间的空格,多个空格值保存一个 21 if(str[Start] == ' ') 22 { 23 str[NewStart++] = str[Start++]; 24 while(Start < End && str[Start] == ' ') 25 { 26 Start++; 27 } 28 } 29 //复制非空格的字符 30 else 31 { 32 str[NewStart++] = str[Start++]; 33 } 34 } 35 //去掉末尾的空格 36 if(str[NewStart-1] == ' ') 37 { 38 str[NewStart-1] = '\0'; 39 } 40 } 41 42 int main() 43 { 44 char str[] = " wo shi Vincent "; 45 remove_extra_space(str); 46 cout<<str<<"@@@@@@"<<endl; 47 return 0; 48 }
3.求1024!的结果中末尾有多少个0
问题分析:首先1024!呀,是多么大的一个数呢??想求出来结果然后在看末尾有多少0是不现实的,我们可以分析要求的是:末尾有多少个0。那么的0的个数有什么决定的呢?10 = 2 * 5 ,显然一对(2 , 5)就能凑出一个末尾0,也就是说末尾0的个数是由因子2和5的个数决定的,更确切的说是由因子的个数决定的(考虑一下为什么吧!?),那么1~1024这1024个数中包含多少个因子5呢?显然这1024个数有的包含1个因子5,有的包含两个因子5(5^2 = 25),有得包含三个因子5(5^3 = 125),有的包含4的因子5(5^4 = 625)。那么一共包含多少因子5呢?
包含一个因子5的数有 1024 / 5 = 204 个。
包含两个因子5的数有 1024 / 25 = 40 个。
包含三个因子5的数有 1024 / 125 = 8 个。
包含四个因子5的数有 1024 / 625 = 1 个。
也就是说1~1024这1024个数一共包含因子5的个数是: sum = 204 + 40 + 8 + 1 个。
小结:注意类似的问题可以借鉴这个题的解题思路~~~~~~
4.给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数
问题分析:现在给了一个能随机生成1~5的随机函数,怎样利用这个已知条件生成一个1~7的随机函数呢?既然要生成的是随机数那么生成1,2,3,4,5,6,7的概率就应该是一样的。显然现在光生成1~5之间的数就不够了,我们想到应该要加大生成数的范围,并且加大范围的同时还要保证每个数产生的概率一样,于是有这样一种方法用这个表达式来扩大生成数范围:rand5()*5+rand5(),新的数据范围变成:6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.并且可以看出来这个25个数出现的可能性是一样的,于是我们可以只用6~26之间的21个数变成1~7这7个数,于是就是要每3个数对应一个数,即:
6,7,8对应1
9,10,11对应2
…………
24,25,26对应7
这种变化对应的方式是(6 - 3)/ 3 = 1,(7 - 3) / 3 = 1,(8-3) / 3 = 1.看看代码吧:
相关代码:
1 int rand7() 2 { 3 int i; 4 //直到产生6~26之间的数跳出循环 5 while((i=rand5()*5+rand5()) > 26) ; 6 return (i-3)/3; 7 }
1 int rand7() 2 { 3 int i; 4 //直到产生6~26之间的数跳出循环 5 while((i=rand5()*5+rand5()) > 26) ; 6 return (i-3)/3; 7 }