一、二分查找
1、查找無序數組中的任意一個局部最小值
局部最小值:arr長度爲1時,arr[0]是局部最小。arr的長度爲N(N>1)時,如果arr[0]<arr[1],那麼arr[0]是局部最小;如果arr[N-1]<arr[N-2],那麼arr[N-1]是局部最小;如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那麼arr[i]是局部最小。
解法:二分查找。先查看左右兩邊是不是局部最小。都不是則局部最小一定在中間,於是查看mid處是不是,若不是則局部最小要麼在其左邊要麼在其右邊,判斷出在左邊還是在右邊,繼續二分查找即可。
class
Solution
{
public
:
int
getLessIndex(vector<
int
>
arr) {
int
length
= arr.size();
if
(length
==
0
)
return
-
1
;
if
(length
==
1
)
return
0
;
if
(arr[
0
]
< arr[
1
])
return
0
;
if
(arr[length-
1
]
< arr[length-
2
])
return
length-
1
;
int
start
=
0
,end
= length-
1
;
int
mid
= (start+end)/
2
;
while
(
1
)
{
if
(arr[mid]
< arr[mid+
1
]
&& arr[mid] < arr[mid-
1
])
return
mid;
else
if
(arr[mid]
> arr[mid-
1
])
{
end
= mid;
mid
= (start+end)/
2
;
}
else
{
start
= mid;
mid
= (start+end)/
2
;
}
}
}
};
2、對於一個有序數組arr,再給定一個整數num,請在arr中找到num這個數出現的最左邊的位置
解法:二分查找。要注意當arr[mid]大於或等於num時都要轉向左半部查找。查找停止條件很重要。
class
LeftMostAppearance
{
public
:
int
findPos(vector<
int
>
arr,
int
n,
int
num)
{
int
res
= -
1
;
int
start
=
0
,end
= n-
1
;
int
mid
= (start+end)/
2
;
while
(start
<= end) {
if
(arr[mid]
< num) {
start
= mid+
1
;
}
else
if
(arr[mid]
== num) {
res
= mid;
end
= mid-
1
;
}
else
{
end
= mid-
1
;
}
mid
= (start+end)/
2
;
}
return
res;
}
};
有序循環數組:1,2,3,4,5 2,3,4,5,1 3,4,5,1,2 4,5,1,2,3 5,1,2,3,4
解法:①判斷最左和最右的關係。確定數組是有序的還是循環過的。
②判斷最左和中間的關係。確定最小值位於左半部還是右半部。
③最左大於中間,在左半部,end = mid;最左小於或等於中間,在右半部,但是start的更新不同,小於時 start=mid,等於時start=mid+1.
int getMin(vector<int> arr, int n) {
if (n == 0) {
return -1;
}
int start = 0,end = n-1;
int mid = start + (end-start)/2;
if (arr[start] < arr[end]) {
return arr[start];
}
else if (arr[start] > arr[end]) {
while (start != end) {
if (arr[start] > arr[mid]) {
end = mid;
}
else if(arr[start] < arr[mid]){
start = mid;
}
else {
start = mid+1;
}
mid = start + (end-start)/2;
}
return arr[start];
}
else {
int min = arr[start];
for (int i = 0;i < n;i++) {
if (arr[i] < min)
min = arr[i];
}
return min;
}
}
};
解法:直接判斷mid是否滿足,arr[mid] >= mid都說明只有左半部有可能出現滿足條件的元素,arr[mid] < mid說明只有右半部有可能。
5、統計完全二叉樹的結點總數
class
Find
{
public
:
int
findPos(vector<
int
>
arr,
int
n)
{
int
res
= -
1
;
if
(n
==
0
)
return
res;
int
start
=
0
,end
= n-
1
;
int
mid
= -1
;
if
(arr[start]
== start)
return
start;
while
(start
<= end) {
mid = start + (end-start)/
2
if
(arr[mid]
== mid) {
res
= mid;
end
= mid-1;
}
else
if
(arr[mid]
> mid)
end
= mid-1;
else
start
= mid +
1
;
}
return
res;
}
};
要求:時間複雜度低於O(N)。
解法:二分查找。
①先遍歷到二叉樹左子樹最左邊的結點和右子樹最左邊的結點,統計左右子樹的深度。
②若左右子樹深度相同,說明左子樹是滿二叉樹,若左子樹深度大於右子樹深度,說明右子樹是滿二叉樹。
③遞歸地進行上述過程。
class
CountNodes
{
public
:
int
count(TreeNode*
root) {
if
(!root)
return
0
;
int
leftDepth
=
0
,rightDepth
=
0
;
TreeNode
*leftL = root,*rightL = root;
while
(leftL
|| rightL) {
if
(leftL)
{
leftDepth++;
leftL
= leftL->left;
}
if
(rightL)
{
rightDepth++;
if
(rightL
== root)
rightL
= rightL->right;
else
rightL
= rightL->left;
}
}
if
(leftDepth
== rightDepth) {
return
count(root->right)
+ pow(
2
,leftDepth-
1
);
}
else
return
count(root->left)
+ pow(
2
,rightDepth-
1
);
}
};
6、快速冪算法
要求:求整數k的N次方的過程請實現時間複雜度爲O(logN)的方法。
解法:N的二進制表示完美地劃分了求冪的過程,二進制有多少位基數base就計算多少次,每一次求base都等於上一次的base的平方,將二進制中爲1的位對應的基數base乘起來就是最後的結果。
class
QuickPower
{
public
:
int
getPower(
int
k,
int
N)
{
long
res=
1
;
long
base=k;
while
(N){
if
(N&
1
)
res=res*base%
1000000007
;
base=base*base%
1000000007
;
N>>=
1
;
}
return
res;
}
};
7、矩陣的快速冪
應用:計算斐波那契數列。