【leetcode 分治法】Pow(x, n)與Sqrt(x)函式的實現
阿新 • • 發佈:2019-01-29
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int 類型範圍 -2147483648~2147483647
當n=-2147483648,則-n=2147483648超出2147483647,結果-n仍然是-2147483648,所以這個情況應該單獨處理。
也可以用機器碼來解釋這一個知識點:-2147483648的機器碼為1000...0000(32位),實現取負:即機器碼各位取反最後加1,得到的還是100....000。
這也是第下面程式碼中 if(n<0 && n!=INT_MIN) return 1.0/pow(x,-n) 為什麼這樣寫的原因,如果寫成if(n<0) return 1.0/pow(x,-n) 則會造成死迴圈
pow(x,n/2)*pow(x,n/2)實際上計算了兩次pow(x,n/2),但是正確的演算法應該是計算出pow(x,n/2)後,賦給val,然後val*val,只計算一次pow(x,n/2)。下面的程式每一處都是pow()*pow(),這樣一來跟x*x*x*x....一個一個乘沒什麼不同。
Sqrt(x)
#2 int sqrt(int x)結果是向下取整的,比如sqrt(10)等於3,故我們要尋找的是1~x中最後一個滿足val*val<x的那個數
#3 用二分查詢法(先參見下面程式碼)
取1~x的中點mid,若mid*mid>x,那麼根應該在1~mid-1之間,如果mid*mid<x,那麼根應該在mid+1~x之間,若mid*mid==x則直接輸出mid; 當然,在果mid*mid<x的情況下,根可能就是mid,但是我們仍然假設根在mid+1~x之間,程式執行下去會發現,mid+1~x的所有數的平方都大於x,最後while退出時first、last都等於mid+1,故while退出要處理,這就是if語句的一個作用
#4 考慮溢位問題 因為mid為int型別,所以mid*mid是有可能溢位的,應該宣告為long long型別。
如果限定只能用int型別,那麼可以將mid*mid<x 寫成 mid<x/mid ,這樣肯定不會溢位
Pow(x,n)
1、分析
函式原型 double pow(double x, int n) ,實現求x的n次方 有一點需要注意:int 類型範圍 -2147483648~2147483647
當n=-2147483648,則-n=2147483648超出2147483647,結果-n仍然是-2147483648,所以這個情況應該單獨處理。
也可以用機器碼來解釋這一個知識點:-2147483648的機器碼為1000...0000(32位),實現取負:即機器碼各位取反最後加1,得到的還是100....000。
這也是第下面程式碼中 if(n<0 && n!=INT_MIN) return 1.0/pow(x,-n) 為什麼這樣寫的原因,如果寫成if(n<0) return 1.0/pow(x,-n) 則會造成死迴圈
2、正解程式碼
分治法<span style="font-size:18px;">double pow(double x, int n) { if(n<0 && n!=INT_MIN) return 1.0/pow(x,-n); else if(n==INT_MIN) {double val=pow(x,-(n/2)); return 1.0/val*val;} else if(n==0) return 1.0; else if(n%2==1) {double val=pow(x,(n-1)/2);return x*val*val; } else {double val=pow(x,n/2);return val*val;} }</span>
3、誤解程式碼
下面的程式碼超時,因為:pow(x,n/2)*pow(x,n/2)實際上計算了兩次pow(x,n/2),但是正確的演算法應該是計算出pow(x,n/2)後,賦給val,然後val*val,只計算一次pow(x,n/2)。下面的程式每一處都是pow()*pow(),這樣一來跟x*x*x*x....一個一個乘沒什麼不同。
<span style="font-size:18px;">double pow(double x, int n) { if(n<0 && n!=INT_MIN) return 1.0/pow(x,-n); else if(n==INT_MIN) return 1.0/(pow(x,-(n/2))*pow(x,-(n/2))); else if(n==0) return 1.0; else if(n%2==1) return x*pow(x,(n-1)/2)*pow(x,(n-1)/2); else return pow(x,n/2)*pow(x,n/2); </span>
Sqrt(x)
1、分析
函式原型 int sqrt(int x),實現求x的平方根。 幾點注意: #1 math標頭檔案裡的是double sqrt(double)等三種版本,leetcode上要實現的是int sqrt(int x),還是不同的#2 int sqrt(int x)結果是向下取整的,比如sqrt(10)等於3,故我們要尋找的是1~x中最後一個滿足val*val<x的那個數
#3 用二分查詢法(先參見下面程式碼)
取1~x的中點mid,若mid*mid>x,那麼根應該在1~mid-1之間,如果mid*mid<x,那麼根應該在mid+1~x之間,若mid*mid==x則直接輸出mid; 當然,在果mid*mid<x的情況下,根可能就是mid,但是我們仍然假設根在mid+1~x之間,程式執行下去會發現,mid+1~x的所有數的平方都大於x,最後while退出時first、last都等於mid+1,故while退出要處理,這就是if語句的一個作用
#4 考慮溢位問題 因為mid為int型別,所以mid*mid是有可能溢位的,應該宣告為long long型別。
如果限定只能用int型別,那麼可以將mid*mid<x 寫成 mid<x/mid ,這樣肯定不會溢位
2、程式碼
分治法,二分查詢class Solution {
public:
int sqrt(int x) { //x不會小於0
if(x==0) return 0;
long long int first=1,last=x;
while(first<last){
long long int mid=(first+last)/2;
if(mid*mid<x) first=mid+1; //long long型別與int型別比較,int被暫時當作long long?
else if(mid*mid>x) last=mid-1;
else return mid;
}
if(first*first>x) return first-1;
else return first;
}
};