二分法题解题技巧:
- 边界条件的考虑,中间划分点,循环终点的设定;
- 一般mid=(left+right)/2,但是 left+right可能造成溢出,所以mid=left+(right-left)/2;
- 演算判断终点条件是否有等于号;
- 特殊情况要列举完全
1.arr中找到这个数出现的最左边的位置
//对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置。
//给定一个数组arr及它的大小n,同时给定num。请返回所求位置。
//若该元素在数组中未出现,请返回 - 1。
//
//测试样例:
//[1, 2, 3, 3, 4], 5, 3
//返回:2
int LeftMostAppearance::leftMostAppearance(vector<int> arr, int n, int num){
int left = 0, right = n-1, mid = 0,res = 0;
while (left <= right){
mid = (left + right) / 2;
if (arr[mid] == num){
res = mid;
right = mid - 1;
}
else if (arr[mid] > num){
right = mid - 1;
}
else{
left = mid + 1;
}
}
return res;
}
2.循环有序数组最小值
//对于一个有序循环数组arr,返回arr中的最小值。有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。比如数组[1, 2, 3, 3, 4],是有序循环数组,[4, 1, 2, 3, 3]也是。
//
//给定数组arr及它的大小n,请返回最小值。
//
//测试样例:
//[4, 1, 2, 3, 3], 5
//返回:1
int CircleArrayMin::circleArrayMin(vector<int> arr, int n){
//方法一:二分法
if (arr.empty()){
return -1;
}
if (arr.size() == 1){
return 0;
}
//完全有序
if (arr[0] < arr[n - 1]){
return arr[0];
}
//最左边大于等于最右边,则最小值在其中
int left = 0, right = n-1, mid = 0;
while (arr[left] >= arr[right]){
mid = (left + right) / 2;
if (right - left == 1)
return arr[right];
if (arr[left] > arr[mid]){
right = mid;
}
if (arr[right] < arr[mid]){
left = mid;
}
}
return arr[mid];
//方法二:数组扫描
/*if (arr.empty()){
return -1;
}
if (arr.size() == 1){
return arr[0];
}
if (arr[0] < arr[n-1]){
return arr[0];
}
for (int i = 0; i < n - 1; ++i){
if (arr[i + 1] < arr[i])
return arr[i + 1];
}
return -1;*/
}
3.求一个整数的n次方
//如果更快的求一个整数k的n次方。
//如果两个整数相乘并得到结果的时间复杂度为O(1),得到整数k的N次方的过程请实现时间复杂度为O(logN)的方法。
//给定k和n,请返回k的n次方,为了防止溢出,请返回结果Mod 1000000007的值。
//
//测试样例:
//2, 3
//返回:8
int GetPowerValue::getPowerValue(int k, int N){
long long tmp = k;
long long res = 1;
int m = 1000000007;
for (; N > 0; N >>= 1){
if ((N & 1) != 0){
res *= tmp;
}
tmp = (tmp*tmp) % m;
res = res % m;
}
return res;
}
4.完全二叉树节点个数
//给定一棵完全二叉树的根节点root,返回这棵树的节点个数。如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。
//给定树的根结点root,请返回树的大小。
//计算2的n次方
int power(int n){
int res = 1;
for (int i = 0; i < n; ++i){
res *= 2;
}
return res;
}
int CountTreeNodes::countTreeNodes(TreeNode* root){
//判断是否左孩子、右孩子均为空,返回只有一个节点
if (root->left == NULL && root->right == NULL){
return 1;
}
//判断是否右孩子为空,返回有两个节点
if (root->right == NULL){
return 2;
}
//遍历左子树与右子树,计算树高度
TreeNode* leftNode = root->left;
TreeNode* rightNode = root->right;
int lHeight = 1;
int rHeight = 1;
//总的节点数
int nodes = 0;
//计算左子树的高
while (leftNode->left != NULL){
++lHeight;
leftNode = leftNode->left;
}
//计算右子树的高
while (rightNode->left != NULL){
++rHeight;
rightNode = rightNode->left;
}
//左子树高大于右子树,则右子树为满二叉树,只需计算左子树
if (lHeight > rHeight){
nodes = power(rHeight);
nodes += countTreeNodes(root->left);
}
//左子树高与右子树高相等,则左子树为满二叉树,只需计算右子树
else{
nodes = power(lHeight);
nodes += countTreeNodes(root->right);
}
return nodes;
}