【100题】第六十六~第七十题(颠倒栈、扑克牌顺子和掷骰子概率、数字数组排成最小数、求旋转数组中最小值、全排列)

一,颠倒栈。

1)题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。

2)分析:乍一看到这道题目,第一反应是把栈里的所有元素逐一pop出来,放到一个数组里,然后在数组里颠倒所有元素,最后把数组中的所有元素逐一push进入栈。这时栈也就颠倒过来了。颠倒一个数组是一件很容易的事情。不过这种思路需要显示分配一个长度为O(n)的数组,而且也没有充分利用递归的特性。

我们再来考虑怎么递归。我们把栈{1, 2, 3, 4, 5}看成由两部分组成:栈顶元素1和剩下的部分{2, 3, 4, 5}。如果我们能把{2, 3, 4, 5}颠倒过来,变成{5, 4, 3, 2},然后在把原来的栈顶元素1放到底部,那么就整个栈就颠倒过来了,变成{5, 4, 3, 2, 1}

接下来我们需要考虑两件事情:一是如何把{2, 3, 4, 5}颠倒过来变成{5, 4, 3, 2}。我们只要把{2, 3, 4, 5}看成由两部分组成:栈顶元素2和剩下的部分{3, 4, 5}。我们只要把{3, 4, 5}先颠倒过来变成{5, 4, 3},然后再把之前的栈顶元素2放到最底部,也就变成了{5, 4, 3, 2}

至于怎么把{3, 4, 5}颠倒过来……很多读者可能都想到这就是递归。也就是每一次试图颠倒一个栈的时候,现在栈顶元素pop出来,再颠倒剩下的元素组成的栈,最后把之前的栈顶元素放到剩下元素组成的栈的底部。递归结束的条件是剩下的栈已经空了。这种思路的代码如下:

template<typenameT>voidReverseStack(std::stack<T>& stack)

{

if(!stack.empty())

{

T top = stack.top();

stack.pop();

ReverseStack(stack);

AddToStackBottom(stack, top);

}

}

我们需要考虑的另外一件事情是如何把一个元素e放到一个栈的底部,也就是如何实现AddToStackBottom。这件事情不难,只需要把栈里原有的元素逐一pop出来。当栈为空的时候,push元素e进栈,此时它就位于栈的底部了。然后再把栈里原有的元素按照pop相反的顺序逐一push进栈。

注意到我们在push元素e之前,我们已经把栈里原有的所有元素都pop出来了,我们需要把它们保存起来,以便之后能把他们再push回去。我们当然可以开辟一个数组来做,但这没有必要。由于我们可以用递归来做这件事情,而递归本身就是一个栈结构。我们可以用递归的栈来保存这些元素。

基于如上分析,我们可以写出AddToStackBottom的代码:

// Add an element to the bottom of a stack:

template<typenameT>voidAddToStackBottom(std::stack<T>& stack, T t)

{

if(stack.empty())

{

stack.push(t);

}

else

{

T top = stack.top();

stack.pop();

AddToStackBottom(stack, t);

stack.push(top);

}

}

先弹出,处理,再恢复,化作子问题的一个重要方式

3)源码:

[html] view plaincopy
  1. #include <iostream>  
  2. #include <stack>   
  3. using namespace std;  
  4.   
  5. template<typename T>  
  6. void ReverseStack(stack<T>& stack)  
  7. {  
  8.     if(!stack.empty())  
  9.     {  
  10.         T top = stack.top();//Pop the top element  
  11.         stack.pop();  
  12.         ReverseStack(stack); // Reverse the remaining stack  
  13.         AddToStackBottom(stack, top); //Add the top element to the bottom of the remaining stack  
  14.     }  
  15. }  
  16. template<typename T>   
  17. void AddToStackBottom(stack<T>& stack, T t)  
  18. {  
  19.     if(stack.empty())  
  20.     {  
  21.         stack.push(t);  
  22.     }  
  23.     else  
  24.     {  
  25.         T top = stack.top();  
  26.         stack.pop();  
  27.         AddToStackBottom(stack, t);  
  28.         stack.push(top);  
  29.     }  
  30. }  
  31. void print(stack<int> mystack)   
  32. {  
  33.     while(mystack.size())  
  34.     {     
  35.         cout<<mystack.top()<<" ";   
  36.         mystack.pop();   
  37.     }   
  38. }  
  39.   
  40. int main()  
  41. {  
  42.     stack<int> mystack;  
  43.     mystack.push(1);   
  44.     mystack.push(2);  
  45.     mystack.push(3);   
  46.     mystack.push(4);   
  47.     mystack.push(5);  
  48.     print(mystack);       
  49.     printf("\n");   
  50.     ReverseStack(mystack);  
  51.     printf("\n");     
  52.     print(mystack);   
  53. }   


二,扑克牌和掷骰子

1)题目:扑克牌的顺子

从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2-10 为数字本身,A 为1,J为11,Q 为12,K 为13,而大小王可以看成任意数字。

2)分析:

思路一:排序法,其中大小王看成0。然后,统计0个数,和数字间隔数,如果有重复(对子)则返回失败。间隔数小于等于2则成功,否则失败。

思路二:Hash法,类似排序。

3)源码:

注意:sort(int array[],array+nLength,comp); //注意添加 <algorithm> using namespace std;

qsort(int array[],nLength,sizeof(array[0]),compare); //自带的

[html] view plaincopy
  1. #include <iostream>  
  2. #include <algorithm>  
  3.   
  4. using namespace std;  
  5.   
  6. //函数功能 : 从扑克牌中随机抽5张牌,判断是不是一个顺子  
  7. //函数参数 : pCards为牌,nLen为牌的张数  
  8. //返回值 :   是否顺子  
  9. int comb(const int *x,const int *y)  
  10. {  
  11.     return x<y;  //  
  12.       
  13. }  
  14. int cmp(const void *x,const void *y)  
  15. {  
  16.     return *(int *)x - *(int *)y;  //  
  17.       
  18. }  
  19.   
  20. bool IsContinuous(int *pCards, int nLen)  
  21. {  
  22.     if(pCards == NULL || nLen <= 0)  
  23.         return false;  
  24.   
  25. //  sort(pCards, pCards + nLen,comb); //调用标准库的排序算法  
  26.       
  27.     qsort(pCards,nLen,sizeof(pCards[0]),cmp);   
  28.       
  29.     //for(int i=0;i<nLen;++i)  
  30.     //   cout<<pCards[i]<<" ";  
  31.       
  32.     int i;  
  33.     int zeroCount = 0;   //大小王用0表示  
  34.     int capCount = 0;    //间隔数  
  35.     //统计0的个数  
  36.     for(i = 0; i < nLen; i++)  
  37.     {  
  38.         if(pCards[i] == 0)  
  39.             zeroCount++;  
  40.         else  
  41.             break;  
  42.     }  
  43.     //统计间隔数  
  44.     int preCard = pCards[i];      
  45.     for(i = i + 1; i < nLen; i++)  
  46.     {  
  47.         int curCard = pCards[i];  
  48.         if(preCard == curCard)  //与前一张牌比较  
  49.             return false;  
  50.         else  
  51.             capCount += curCard - preCard - 1; //累加间隔数  
  52.         preCard = curCard;  
  53.     }  
  54.     return (zeroCount >= capCount)? true: false; //只要王的个数大于间隔数  
  55. }  
  56. int main()  
  57. {  
  58.     int pCards[]={1,2,3,4,5};  
  59.     int pCards2[]={1,2,8,4,12};  
  60.       
  61.     int i=IsContinuous(pCards,5);  
  62.     bool j=IsContinuous(pCards2,5);  
  63.       
  64.     cout<<i<<endl;  
  65.     cout<<j<<endl;  
  66. }  


1)题目:n 个骰子的点数。把n 个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,

2)分析:

PN,S来表示N个骰子,顶面数字之和为S出现的概率,则:

PN,S=aN,S/6^N

其中aN,SN个骰子顶面数之和为S的状态的数量,6^N为总的状态数量,因为每个骰子有6种状态,N个骰子组成的状态数就是6^N

下面给出求ai,j的递推公式,即求i(1=<i<=N)个骰子顶面数之和为j(i=<j<=6*i)时的状态数量:

ai,j= ai-1,j-1 //第i个骰子顶面为1时,其他i-1个骰子顶面数之和为j-1时的状态数量

+ ai-1,j-2 //第i个骰子顶面为2时,其他i-1个骰子顶面数之和为j-2时的状态数量

+ ai-1,j-3 //第i个骰子顶面为3时,其他i-1个骰子顶面数之和为j-3时的状态数量

+ ai-1,j-4 //第i个骰子顶面为4时,其他i-1个骰子顶面数之和为j-4时的状态数量

+ ai-1,j-5 //第i个骰子顶面为5时,其他i-1个骰子顶面数之和为j-5时的状态数量

+ ai-1,j-6 //第i个骰子顶面为6时,其他i-1个骰子顶面数之和为j-6时的状态数量

其中递推公式中i>1

对于任意的1=<i<=Nj<=0j<ij>6*iai,j=0

3)源码:

非递归求解:

[html] view plaincopy
  1. #include <iostream>  
  2. #include <math.h>  
  3.   
  4. using namespace std;  
  5.   
  6. void PrintSumProbabilityOfDices(unsigned int n)    
  7. {    
  8.     const unsigned int MAX=12;  //max number of dices     
  9.     if(n>MAX)    
  10.     {    
  11.         printf("Overflow!\n");    
  12.         return;    
  13.     };    
  14.     
  15.     unsigned int a[MAX+1][6*MAX+1];    
  16.     unsigned int i,j,k;    
  17.     memset(a,0,sizeof(a));   
  18.        
  19.      /* a[i][j]  i代表骰子个数  j代表 i个骰子可能出现的骰子的数量   
  20.      *   
  21.      */   
  22.     for(j=1;j<=6;j++)//只有一个骰子的时候,每一个数出现的次数都是 1    
  23.         a[1][j]=1;    
  24.           
  25.     for(i=2;i<=n;i++) //第 i个 骰子出现   
  26.         for(j=i;j<=6*i;j++)  //j为所有骰子可以出现的次数   
  27.         {    
  28.             a[i][j]=0; //初始化要求的 数   
  29.             for(k=1;k<=6&&k<=j;k++)  //k代表 第 i个骰子出现的可能点数  k<=j 说明 不能超过当前所求所有骰子 总点数   
  30.                 a[i][j]+=a[i-1][j-k];  //其他 i-1个骰子 出现j-k 点的次数   
  31.         }    
  32.     
  33.     unsigned int nTotal=pow(6,n);   
  34.        
  35.     for(i=n;i<=6*n;i++)    
  36.         printf("Sum=%d,Probability=%.15lf\n",i,a[n][i]*1.0/nTotal);    
  37. }    
  38.   
  39. int main()  
  40. {  
  41.     PrintSumProbabilityOfDices(2);  
  42. }  

三,把数组排成最小的数。【经典牛逼的题】

1)题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。

例如:输入数组{32, 321},则输出这两个能排成的最小数字32132。

请给出解决问题的算法,并证明该算法。

2)分析:这是09 年6 月份百度的一道面试题,

思路:根据“字典序”排序,先比较整数中第一个数字,然后再比较第二个,直到所有数都成字典序排列。

很遗憾】这个是不可行的,比如32的字典序比322小,但是32322比32232大

那该如何是好?

主要是根据题目要求,先排列好顺序,然后按照顺序输出每个数。根据“字典序”排序已经被排除了,那么如何选择排序规则呢?

【巧妙】所以在这里自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。

如果用char *表示字符串,那就可以使用qsort函数进行排序了,我这里用的是string,所以自定义了一个最简单的冒泡排序。

3)源码:

注意,int 转换为 string

[html] view plaincopy
  1. #include<iostream>  
  2. #include<algorithm>  
  3. #include<string>  
  4. #include<sstream>  
  5. using namespace std;  
  6.   
  7. int compare1(string str1,string str2)//user defined compare function  
  8. {     
  9.     string tmp1 = str1.append(str2);  
  10.     string tmp2 = str2.append(str1);  
  11.     if(tmp1.compare(tmp2)>=0)//tmp1 > tmp2   
  12.         return true;  
  13.     return false;  
  14. }  
  15. void sort(string str[],int len)//Bubble sort  
  16. {  
  17.     for(int i=len;i>0;i--)  
  18.     {  
  19.         int exchange=0;   
  20.         for(int j=0;j<i;j++)  
  21.         {  
  22.             if(compare1(str[j],str[j+1]))  
  23.             {  
  24.                 string tmp = str[j];  
  25.                 str[j] = str[j+1];  
  26.                 str[j+1] = tmp;  
  27.                 exchange=1;   
  28.             }  
  29.             if(exchange==0)  
  30.                 return;   
  31.         }  
  32.     }  
  33. }  
  34. int main()  
  35. {     
  36.     int num[] = {332,41,322,32,414,4};  
  37.     int len = sizeof(num)/sizeof(int);  
  38.   
  39.   
  40.     //string *word = new string[len];  
  41.     string word[len];  
  42.     stringstream sst;  
  43.     for(int i=0;i<len;i++)//convert int to string  
  44.     {  
  45.           
  46.         sst<<num[i];  
  47.         sst>>word[i];  
  48.         sst.clear();  
  49.            
  50.     /*  第二种方法 将int 变为 string   
  51.         char *temp;   
  52.         sprintf(temp,"%d",num[i]);   
  53.         string str(temp);   
  54.         word[i]=str;   
  55.     */   
  56.      /* 第三种方法 将 int 变为 string   
  57.         char *temp;   
  58.         itoa(num[i],temp,10);   
  59.         string str(temp);   
  60.         word[i]=str;   
  61.      */   
  62.       
  63.     }  
  64.   
  65.     sort(word,len);//Bubble sort array of string by the rule defined by compare1  
  66.       
  67.     for(int i=0;i<len;i++)  
  68.     {  
  69.         cout<<word[i];  
  70.     }  
  71.     getchar();  
  72.     return 0;  
  73. }  

四,旋转数组中的最小元素。

1)题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。

例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。

2)分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(N)。

二分查找法,这道题类似于“在已排序数组,旋转后,再查找某个数”这道题。

中值 > 数组开始 则 在右侧查找 3, 4, 5, 1, 2

中值 < 数组开始 则 在左侧查找(含中值) 4, 5, 1, 2, 3

退出条件:仅仅剩余一个值或者为顺序数组则取最小值

3)源码:

[html] view plaincopy
  1. #include <iostream>  
  2. using namespace std;   
  3. int helper(int a[], int s, int t)   
  4. {  
  5.   
  6.     if (s == t || a[s] < a[t])   
  7.          return a[s];  
  8.   
  9.     int m = s + (t-s)/2;  
  10.   
  11.     if (a[s]>a[m]) //中值小    
  12.         return helper(a, s, m);  
  13.     else   
  14.         return helper(a, m+1, t);  
  15.   
  16. }  
  17. int main()  
  18. {  
  19.     int a[]={3,4,5,1,2};   
  20.     int result=helper(a, 0, 4);  
  21.     cout<<result<<endl;   
  22. }   

 

下面的源码为自己原始代码,有些地方多考虑了,注意观察

注意:除以2 为右移 1 位

[html] view plaincopy
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. int GetMinNum(int num[],int begin,int end)  
  5. {  
  6.     if(begin==end ||num[begin]<num[end])  
  7.         return num[begin];   
  8.           
  9.     int mid=begin +(end-begin)>>1;  
  10.     if(num[mid]>num[begin]) //右侧           
  11.          return min(GetMinNum(num,mid+1,end) , num[begin]);           
  12.     else  
  13.          return min(GetMinNum(num,0,mid) , num[mid+1]);   
  14.        
  15. }  
  16. int main()  
  17. {     
  18.     int num[] = {3,4,5,1,2};  
  19.     int MinNum=GetMinNum(num,0,4);   
  20.     cout<<MinNum<<endl;  
  21.       
  22.     getchar();  
  23.     return 0;  
  24. }  

 

五,给出一个函数来输出一个字符串的所有排列。参考

分析:

        1) 每个元素依次放到首位,然后对其余元素递归

        2) 当当前元素到达末尾的时候,输出该序列

       相当于strlen(str)层循环,每层strlen(str)次。

源码一:注意应要使用数组参数char  str[],而不是用变量char  *str

                注意for 循环,第一个是 s ,就是说从第一个开始交换

[html] view plaincopy
  1. #include  <iostream>  
  2. using namespace std;  
  3. void MySort(char str[],int s,int e)  
  4. {  
  5.     if(s==e)  
  6.         cout<<str<<endl;              
  7.     else  
  8.     {  
  9.         for(int i=s;i<e;++i)  
  10.         {  
  11.             swap(str[s],str[i]);   
  12.             MySort(str,s+1,e);  
  13.             swap(str[s],str[i]);   
  14.         }         
  15.     }          
  16. }  
  17. int main()  
  18. {     
  19.     char str[]="abc";   
  20.     MySort(str,0,3);  
  21.     return 0;  
  22. }  

 

扩展:

         如果不是求字符的所有排列,而是求字符的所有组合,应该怎么办呢?(P72

        思路:这里不采用交换方式,而是采用删减的方式。采用不同的剔除顺序,并用prex保留删减值,这样将得到所有符合条件的序列

 

[html] view plaincopy
  1. #include  <iostream>  
  2. using namespace std;  
  3. void MySort(string str,string prex)  
  4. {  
  5.         string temp=str;   
  6.         cout<<prex<<endl;   //注意这里输出           
  7.         for(int i=0;i<str.length();++i)  
  8.         {  
  9.               
  10.             MySort(str.erase(i,1),prex+str.at(i)); //注意这里处理   
  11.             str=temp;   
  12.         }         
  13.            
  14. }  
  15. int main()  
  16. {     
  17.     string str ="abc";   
  18.     MySort(str,"");  
  19.     return 0;  
  20. }  

原文地址:点击打开链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值