如果l和r都在整型範疇裡,mid在l,r之間,首先,這個結果是不會溢出的。
對于l + (r - l) / 2,計算過程中,r- l 使用減法,不會超出最大的整型範疇,同時, l + (r - l) / 2 中的這個加法,由于上面所說,結果不會整形溢出,這個加法也是安全的。
但是對于(l + r) / 2,l + r是一個臨時的計算結果,這個結果即使對于l和r都在整型範疇裡,也可能溢出
自己使用極端值,試一試,體會一下
有幾個朋友給二分求中點mid = (l + r) / 2提出了一些疑問和改進。自己最近也對這個問題有過一些思考,是以在這裡系統詳細地聊聊自己的看法,看看程式中求區間終點到底應該怎麼寫才是完美的。
先公布程式中求區間中點的完美寫法:mid = l + (r - l) / 2。
在數學上,mid = l + (r - l) / 2和mid = (l + r) / 2是等價的,這個自己通分一下就行了。但是在程式中,往往需要求一個數組區間的中點,這也就意味着區間必須是一個整數,如果中點是x.5這種情況,你是向下取整還是向上取整。這在程式設計中是一個很大的坑。
mid = (l + r) / 2的劣勢
首先看看mid = (l + r) / 2這種求中點有什麼劣勢。
1:溢出
2:求上下界會不統一
溢出
溢出比較好了解,l + r可能會溢出int的最大範圍,而l + (r - l) / 2不會,這裡用減法替代了加法,這種思想很多地方都有用到,比如求最小一個數,這個數的平方大于或等于給定的一個值n,直覺代碼的寫法如下:
int x;
for (x = 0; x * x < n; ++x) {}
循環跳出,x就是答案,但是如果n = (1 << 31) - 1呢?這個循環永遠不會結束,可以試試看,原因就是x * x會大過int的最大值,進而導緻溢出出現負數。正确的寫法如下:
int x;
for (x = 0; x < n * 1.0 / x; ++x) {}
這就是利用除法代替乘法,規避了溢出的分險,從數學的角度來看,這兩者等價,但是在程式設計的領域中差别很大。下面分享一個題,你就更能體會這種思想了。