4.5.1 二分查詢
乙個經典的問題:如何在乙個嚴格遞增序列a中找出給定的數x
最直接的辦法是:線性掃瞄序列中的所有元素,如果當前元素恰好為x,則表明查詢成功;如果掃瞄完整個序列都沒有發現給定的數x,則表明查詢失敗,說明序列中不存在數x。這種順序查詢的時間複雜度為o(n)。更好的辦法便是使用二分查詢:二分查詢是基於有序序列的查詢演算法,該演算法一開始令[left,right]為整個序列的下標區間,然後每次測試當前[left,right]的中間位置mid=(left+right)/2,判斷a[mid]與與查詢的元素x的大小:①如果a[mid]==x,說明查詢成功,退出查詢。②如果a[mid]>x,說明元素x在mid位置的左邊,因此往左區間[left,mid-1]繼續查詢。③如果a[mid]#include
using namespace std;
intbinarysearch
(int a,
int left,
int right,
int x)
return-1
;}intmain()
;printf
("%d %d\n"
,binarysearch
(a,0
,n-1,6
),binarysearch
(a,0
,n-1,9
));return0;
}注:如果二分上屆超過int型資料範圍的一半,那麼當欲查詢元素在序列較靠後的位置時,語句mid=(left+right)/2中的left+right就有可能超過int而導致溢位,此時一般使用mid=left+(right-left)/2這條等價語句作為代替以避免溢位。
另乙個問題:如果遞增序列a中的元素可能重複,那麼如何對給定的欲查詢元素x,求出序列中第乙個大於等於x的元素的位置l以及第乙個大於x的元素的位置r,這樣元素x在序列中的存在區間就是左閉右開區間[l,r)
(1)求序列中的第乙個大於等於x的元素位置
①如果a[mid]>=x,說明第乙個大於等於x的元素的位置一定在mid處或mid的左側,應往左子區間[left,mid]繼續查詢,即令right=mid
②如果a[mid]<=x,說明第乙個大於等於x的元素的位置一定在mid處或mid的右側,應往右子區間[mid+1,right]繼續查詢,即令leftt=mid+1
int
lower_bound
(int a,
int left,
int right,
int x)
else
}return left;
}
(2)求序列中的第乙個大於x的元素位置
①如果a[mid]>x,說明第乙個大於x的元素的位置一定在mid處或mid的左側,應往左子區間[left,mid]繼續查詢
②如果a[mid]<=x,說明第乙個大於x的元素的位置一定在mid處或mid的右側,應往右子區間[mid+1,right]繼續查詢
int
upper_bound
(int a,
int left,
int right,
int x)
else
}return left;
}
lower_bound函式和upper_bound函式都在解決乙個問題:尋找有序序列中第乙個滿足某條件的元素的位置。
尋找有序序列第乙個滿足某條件的元素的位置的固定模板
(1)二分區間為左閉右閉的,初值必須能覆蓋解的所有可能取值
int
sovle
(int left,
int right)
else
}return left;
}
如果要尋找最後乙個滿足「條件c」的元素的位置,則可以先求第乙個滿足「條件!c」的元素的位置,然後將該位置減1即可。
(2)二分區間為左開右閉的
int
sovle
(int left,
int right)
else
}return right;
}
4.5.2 二分法拓展
const
double eps=le-5;
//精度為10^-5
doublef(
double x)
double
calsqet()
else
}return mid;
}
計算√2的近似值的問題是這樣乙個問題的特例:給定乙個定義在[l,r]上的單調函式f(x),求方程f(x)=0的根。
const
double eps=le-5;
//精度為10^-5
doublef(
double x)
double
calsqet
(double l,
double r)
else
}return mid;
}
#include
#include
using namespace std;
const
double pi=
acos(-
1.0)
;const
double eps=le-5;
doublef(
double r,
double h)
double
solve
(double r,
double r)
else
}return mid;
}int
main()
#include
using namespace std;
intsolve
(int a,
int left,
int right,
int k)
printf
("num=%d\n"
,num);if
(numelse
}return left;
}int
main()
;int n,k,len;
scanf
("%d"
,&k)
; len=
solve
(a,0
,a[2
],k)
;printf
("長度為%d"
,len-1)
;}
4.5.3 快速冪
接下來研究一下快速冪的迭**法。
對ab來說,如果把b寫成二進位制,那麼b就可以寫成若千二次冪之和。例如13的二進位制是1101,於是3號位、2號位、0號位就都是1,那麼就可以得到13=23 +22+20=8+4+1,所以al3=a8+4+1=a8*a4*a1.
通過上面的推導,我們發現al3可以表示成a8、a4、a1的乘積。很容易想象,通過同樣的推導,我們可以把任意的ab表示成an、… a8、a4、a2、a1中若干項的乘積,其中如果b的進製的i號位為1那麼項a2i就被選中.於是可以得到計算ab的大致思路:令i從0到k枚類b的二進位制的每一位,如果當前位為1,那麼累積a2i.注意到序列an、… a8、a4、a2、a1的前一項總是等於後一項的平方,因此具體實現的時候可以這麼做:
①初始令ans等於1,用來存放累積的結果。
②判斷b的二進位制末尾是否為1 (即判斷b& 1是否為1,也可以理解為判斷b是否為奇數),如果是的話,令ans乘上a的值。
③令a平方,並將b右移一位(也可以理解為將b除以2)。
④只要b大於0,就返回②。
typedef
long
long ll;
ll binarypow
(ll a,ll b,ll m)
a=a*a%m;
b>>=1;
}return ans;
}
演算法導論學習筆記 (1)
乙個acm若菜,趁著acm淡季,開始學習演算法導論了,經過一年的acm學習,逐漸的發現,學東西,深入才是王道,以前學習乙個演算法,總是看懂了就開始做題,到後來才發現很多題目,會演算法,卻不知道是用這個演算法,這就是演算法理解的不到位的後果,從今天開始,定下目標 1.深入系統的學習演算法,2.學會紙上...
演算法導論學習筆記(2)
big o notation 模擬為小於等於 n2 o n o n2 big omega notation 模擬為 大於等於 模擬為等於 嚴格符號 小o與小 模擬為小於和大於 解遞迴方法 1 替換法 guess the form,verify by induction,solve the const...
《演算法導論》學習筆記(1)
時間複雜度 time complexity 0,1,1,2,3,5,8,13,21,34 數列中每個數都是其兩個直接前項的和。f nf fn 的生成規則 f n left f f n 1 1 n 1 0 n 0 end right.fn fn 1 fn 1 10 n 1n 1n 0 fibonacc...