《演算法導論》學習筆記

2021-09-13 01:43:41 字數 4141 閱讀 9643

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...