您的位置:首页 > 其它

不容小视的算法

2015-11-16 16:50 267 查看
自从看了cnblog上那篇讲“野生”程序员的文章,我也时常反思作为科班出身的自己,是不是还带着一些“野生”的做派。我们往往80%的时间在做一些纯业务的事情上,而往往百分之90的时间里面我们的业务量不足够大到需要考虑性能的地步。每天重复地活着,有什么意义呢?

所以常常有人说,算法和数据结构,设计模式,都是并没有什么卵用。我一直对这种观点很反感,计算机编程是 一门科学,科学在于高效。如果我们不考虑大流量、高并发,我们可以很任性地写任何功能,大不了加载缓慢。我们忽略那些看似不重要,或者用不上的黑科技,以为一直呆在地球表面上是最安全的。

我在知乎里面写过一个回复:我见过十年的码畜,也见过一年成架构的神人。之所以有的人用一年走完别人十年走不完的技术道路,是因为关注代码和业务背后的细节,不断积累迭代。最近在看《代码之美》这本书,有些感触。我用一个最不被大家重视的算法为例:

二分法查找。

我对这个算法记忆犹新,大一的时候曹海老师在纸上给我推演了其中过程。我用java代码实现如下:

public class TestBinary {
/**
* 二分法查询
*
* @param arr
* @param value
* @return
*/
public static int binary(int[] arr, int value) {
if (arr.length <= 0) System.out.println("no array to search");
int low = 0;// 低位
int high = arr.length -1; //高位
if (low <= high) {
int mid = (low + high) /2;
if (arr[mid] == value) return mid;
if (arr[mid] > value) {
high = mid - 1;
}
if (arr[mid] > value) {
low = mid + 1;
}
}
return -1;
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] a = {1, 2, 9, 33, 43, 52};
int value = binary(a, 9);
System.out.println(value);
}

}


这是最常规的写法,看上去也没什么问题。但是如果我们要查找的数组变得越来越大,就会出现性能和整型溢出的问题。先说性能吧,其中int mid = (low+high)/2;这句在循环中,不断地在栈上分配临时变量,这也是一种消耗,可以把它写在循环外。溢出问题也是在这句,如果high变量(也就是数组长度)足够大,超过32位机器的2^31 -1的局限,或者64位机的整型极限,那么就会溢出为负数。有人建议写成:

mid = (low + high) >>> 1;

用位运算来防止整型溢出。对位运算不太熟悉的童鞋可以去看看相关资料。

很多人看到这里就感觉皆大欢喜的happy ending,但是我还不想就这么草率地结束。

这里面就存在一个悖论,java不能造出一个大于最大整型数值长度的数组,那么我该如何检验这个溢出问题呢。而且凭什么说用>>> 1就可以解决。我freephp第一个不服!

如果一直纠结造出数组来验证,那么就走不通了。所以我们换个思路,其实本质就是很大的两个数相加除2或用位运算会不会整型溢出的测试。代码如下:

System.out.println("除以2:"+ ((1000000000+Integer.MAX_VALUE) /2));

System.out.println("除以2:"+ ((1000000000+Integer.MAX_VALUE) >>> 1));

输出结果为:

除以2:-573741824

除以2:1573741823

可见位运算可以保证整型不溢出。

(待续ing)

.-' _..`. / .'_.'.' | .' (.)`. ;' ,_ `. .--.__________.' ; `.;-'| ./ /| | / `..'`-._ _____, ..' / | | | |\ \ / /| | | | \ \ / / | | | | \ \ /_/ |_| |_| \_\ |__\ |__\ |__\ |__\
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: