这两天,我在看《编程之美》,这是一本微软的算法面试题集,里面的算法都是比较经典的。
我没有全部看完,因为我没那么多时间,也没有那样的耐心。故我只看了第二章和第三章,因为这两部分内容貌似比较有趣些。好吧,下面说说我看这本书的过程和一些心得吧。
开始看的时候,第一题便是统计一个字节里面1的个数。我的第一感觉就是不断右移直至这个数字为0。如下所示:
int Count(BYTE v)
{
int num = 0;
while(v)
{
num+=v&0x01;
v>>1;
}
return num;
}
后来,这本书提到,复杂度能不能只与1有关呢?好戏来了。我们可以从右面算起,凡是有1的话,就将它统计完毕之后消灭。如1010,这个数字,我们要怎么消灭右面的1呢?我们知道,在十进制数11000,若将千位上的1消灭,可以将1000变小一点点,至于减多少呢?因为后面我们要将百位十位和各位上的数消灭掉。这时候,我们想到“&”这个运算符,它可以将1变0.于是,我们决定减去1。所以二进制1010,减去1后变成1001,然后我们可以用1010跟它&运算。即1010&1001=1000.好吧,原来数字1010的1前面不变,后面(包括它)全为0了。具体代码如下:
while(v)
{
v&=v(v-1);
num++;
}
好吧,有点意识到这个算法的巧妙性了。
——————————–我是分割线——————————
算法就不一一道来了,选一些较为经典而巧妙的。
最大公约数
一开始看到这个问题,感觉哇,这也是问题?确实,辗转相除法貌似已经足够强大了,或者互相相减也是可以的。但是有没有一种方法可以将这两个算法的有点结合起来呢?我们从公约数的特点分析:
1 对于x和y来说,如果y = k *y1, x =k*x1,那么会有f(x,y) = k*f(x1,y1).
2, 如果x=p*x1, 而p是素数,并且y%p!=0.那么f(x,y)=f(p*x1,y) = f(x1, y);
因此,我们可以利用p=2,不断地将范围缩小。因为除以2的话,我们可以直接右移就可以了,免去了取模的庞大开销。
若x和y均是偶数的话,f(x,y)= 2*f(x/2,y/2) = 2*f(x>>1, y>>1);
若x和y中又一个是偶数,则可以将其中一个缩小一半,即x>>1或者y>>1.其余不变。
若均为奇数,则f(x,y) = f(y,x-y);
总的 来说,最坏情况下的时间复杂度是O(log(max(x,y))).代码就不贴了。有上面的伪代码,很快可以写出来了。
———————————————————我也是分割线———————————————
字符串移位包含问题:给定两个字符串str1, str2.要求判定str2是否能通过str1做循环移位之后得到。如str1 = AABCD, str2 =CDAA.则返回true。
一开始的时候,是那种逐个逐个移位,然后用String.h里面的strstr()搜索是否有字串。这种方法十分不好,很是浪费时间,因为有数组移位和重复计算。代码如下:
for (i=0;i<len(str1);i++)
{
temp = str1[0];
for(j=0;j<len-1;j++)
str1[j] = str1[j+1];
src1[j] = temp
if(strstr(str1,str2))
return true;
}
return false;
这是比较笨的方法。我们可以先对str1进行右移,但是不将前面的数字拿走。AABCD 移位之后变成AABCDAABCD.然后这就是str1移位的所有结果。这个时候才来判定是否包含str2。一次就能搞定了。YEAD!
———————————–
另外还有那个计算字符串的相似度,我觉得很不错的算法。对于串A和串B来说,从头开始比较,若不相等,则有三种方案,要么删去其中一个字符,要么增加相应字符,要么修改它。这样一来,两个字符串前面的字符均是一样的。我们可以用递归来解决剩余字符串。代码有点长,不贴了。
————————————-
无头单链表删除节点。
一开始,我怎么也想不到,想不到居然还可以删除指定节点的下一个节点,只要将节点的值交换过来就可以了。这是醒目至极。
———————————————————end—————————————————–
有点乱,代码很少,都不知道自己以后再返回来看会不会看得懂。好吧,就到这里了,休息一下。