一道面试题的探讨(二)

太长了,装不下,分两页。

 

 

解法三:

解法二占用的空间已经很小了,但是时间复杂度还是太高。另外每次输出的循环里都有一个判断是否为”11”的。从汇编的角度讲,这个是很讨厌的:一条判断语句就代表比较语句、标志位的改变、跳转语句等;如果将其改为位操作的话能提高不少效率。具体算法如下:

void NonRecursive2( char* source, int N )

{

                BitSet B[2*N] = {0} ;

                for (; B <= {11111...} ; B ++ )

                {

                                std::string strTemp ;

                               

                                bit c = 0;

                                for ( int i=0; i < N; i++ )

                                {

                                                //if there is one {11}, after {11} XOR {11}, we get 00

                                                //negate it to one bit, we get 1

                                                //c then becomes 1

                                                c |=  !( B[2*i, 2*i+1] XOR {11} ) ;

                                }

 

                                if ( c != 1 )

                                {

                                                int indexX = 0 ;

                                                int indexY = 0 ;

                                                for ( int i=0; i < N; i++)

                                                {

                                                                indexX = source[i] - 48 ;

                                                                indexY = B[2*i, 2*i+1] ;

                                                                strTemp += g_telephone[ indexX ][ indexY ] ;

                                                }

                                                std::cout<< strTemp<<std::endl;

                                }                             

                }

}

 

时间复杂度:时间复杂度还是O(N*4^N)。但是此改进能大大减少常数系数,可能会有几倍的速度提高(我没试过,有兴趣的试一试)。

空间复杂度: O(N)

 

解法四:

进一步考虑问题,问题的关键主要集中于如何降低时间复杂度。为什么时间复杂度里会出现系数N?是因为我们每次都遍历一遍数组打印结果。可是真的有必要每次都把长度为2N的数组遍历一遍么?考虑最简单的情况,假设一个2N的数组的值为全零,这时候加1,这时我们发现该数组的前2N-1位都没变,只有最后一位变了;也就是说,我们如果将前(N-1)个字符缓存,只需要加上最后一个字符就行了。

问题就转化为,找到自右向左第一个零出现的位置;零的左边原样输出,右边变化。具体算法如下:

void NonRecursive3( char* source, int N )

{

                BitSet B[2*N] = {0} ;

                std::string strLeft = "";

                int zeroPos = N ;

                for (; B <= {11111...} ; B ++ )

                {

                                std::string strTemp = strLeft;

                               

                                bit c = 0;

                                for ( int i=0; i < zeroPos; i++ )

                                {

                                                //if there is one {11}, after {11} XOR {11}, we get 00

                                                //negate it to one bit, we get 1

                                                //c then becomes 1

                                                c |=  !( B[2*i, 2*i+1] XOR {11} ) ;

                                }

 

 

                                if ( c != 1 )

                                {

                                                int indexX = 0 ;

                                                int indexY = 0 ;

                                                for ( int i=0; i < zeroPos; i++)

                                                {

                                                                indexX = source[i] - 48 ;

                                                                indexY = B[2*i, 2*i+1] ;

                                                                strTemp += g_telephone[ indexX ][ indexY ] ;

                                                }

                                                std::cout<< strTemp<<std::endl;

                                }

 

                                //find the first zero position from right to left

                                zeroPos = FindFirstZero(B) ;

                                //return the unchanged right part

                                strLeft = Split(strTemp, zeroPos) ;

                }//for

}

 

时间复杂度:时间复杂度的计算有点复杂。考虑这样一个组合问题,有2N个位置,前k个位置每次固定,剩下的(2N-k)个位置看做是需要变化的位置,则对于给定前k个位置的情况,复杂度为(2N-k)/2。而考虑前k个位置的情况,总共有2^k种可能。所以总的时间复杂度是(2^1*(2N-1)/2 + 2^2*(2N-2)/2 + … + 2^(2N-1)*1/2)。等差比数列,用高中的数学算出来是(4^N-2N),感兴趣的可以重新算一遍看我有没有算错。

空间复杂度: O(N)

 

解法五:

最后一种方法,是考虑到用2bits来表示三种字符,”11”那一位就浪费了。如果想进一步节省空间的话,可以用位数组表示3^N个数,用除数和余数来表示位置和字符信息。

举个简单的例子,比如输入是”12”,用B[4]来表示可能的字符串。之所以取长度为4是因为需要表示3^2=9个数,2^3=8不够表示。接下来要做的就是二进制与三进制的转化。比如00112 = 00013

二进制的乘法和除法可以参考微机原理的书,我现在只记得乘法的处理方式:B[N]*3 = B[N]<<1 + 1。具体算法如下:

void NonRecursive4( char* source, int N )

{

                //use 3 as dimension, the largest number will not extend 2*N

                //use a structure to automatically malloc B

                BitSet B1 = {1} ;

 

                for ( int i=0; i<N; i++ )

                {

                                //find the largest position

                                B1 = B1<<1 + B1 ;

                }

 

                BitSet B[B1.getLength] = {0} ;

 

                std::string strLeft = "";

                int zeroPos = B1.getLength ;

                for (; B <= B1 ; B ++ )

                {

                                std::string strTemp = strLeft;

                               

                                int indexX = 0 ;

                                int indexY = 0 ;

 

                                BitSet B2 = B[0..zeroPos] ;

                                int i = 0

                                while ( B2 != 0 )

                                {

                                                indexX = source[i++] - 48 ;

                                                indexY = B2 % 3 ;                                             

 

                                                strTemp += g_telephone[ indexX ][ indexY ] ;

                                                B2 = B2/3 ;

 

                                }

                                std::cout<< strTemp<<std::endl;

 

                                //find the first zero position from right to left

                                zeroPos = FindFirstZero(B) ;

                                //return the unchanged right part

                                strLeft = Split(strTemp, zeroPos) ;

                }//for

}

 

时间复杂度:没高兴算,应该也是O(N*3^N)量级的。

空间复杂度: O(N)

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值