一、二分查找
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、矩阵的快速幂
应用:计算斐波那契数列。