1. 二分搜索要点
2. 二分搜索例题
- 无序数组的局部最小值查找:
局部最小值位置查找:
定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0]
//无序数组返回任一局部最小位置
public class Solution {
public int getLessIndex(int[] arr) {
//三种情况 长度大于1时
//如果0号元素小于1号元素返回0 如果n-1号元素小于n-2号元素返回n-1
//第三种情况,使用二分查找
//二分查找中间元素 若中间元素同时小于两侧元素则返回中间元素索引
//若中间元素小于右边元素,则在左半部分再进行二分查找 若中间元素大于左边元素,则在右边进行
//若中间元素既大于左边又大于右边,则两边都有可能
if(arr==null||arr.length==0){
return -1;
}
if(arr.length==1||arr[0]<arr[1]){
return 0;
}
if(arr[arr.length-1]<arr[arr.length-2]){
return arr.length-1;
}
int mid,left=1,right=arr.length-2;
while(left<right){
mid=(left+right)/2;
if(arr[mid]>arr[mid-1]){
right=mid-1;
}else if(arr[mid]>arr[mid+1]){
left=mid+1;
}else{
return mid;
}
}
return left;
}
}
- 寻找一个数出现在有序数组中最左边的位置
import java.util.*;
//寻找一个数出现在有序数组中最左边的位置
public class LeftMostAppearance {
public int findPos(int[] arr, int n, int num) {
int left=0,right=n-1,mid,index=-1;
//此处需要加上等号
while(left<=right){
mid=(left+right)/2;
if(arr[mid]==num){
index=mid;
}
//因为是寻找最左边的,如果当前数大于等于给定数就在左半边寻找
if(arr[mid]>=num){
right=mid-1;
}
else{
left=mid+1;
}
}
return index;
}
}
- 有序循环数组的最小值寻找
import java.util.*;
//有序循环数组寻找最小值
public class MinValue {
public int getMin(int[] arr, int n) {
if(arr==null||arr.length==0){
return -1;
}
int l=0,r=n-1,mid;
while(l<r){
//左边界与右边界相邻时,退出循环,选择左右边界最小值返回
if(l==(r-1)){
break;
}
if(arr[l]<arr[r]){
return arr[l];
}
mid=(l+r)/2;
if(arr[l]>arr[mid]){
r=mid;
continue;
}
if(arr[mid]>arr[r]){
l=mid;
continue;
}
while(l<r){
if(arr[l]==arr[mid]){
l++;
}
else if(arr[l]>arr[mid]){
r=mid;
break;
}else{
return arr[l];
}
}
}
return Math.min(arr[l],arr[r]);
}
}
- 有序不重复数组上寻找最左位置
import java.util.*;
//有序不重复数组上寻找最左位置
public class Find {
public int findPos(int[] arr, int n) {
if(arr[0]>n-1){
return -1;
}
if(arr[n-1]<0){
return -1;
}
int left=0,right=n-1,mid,res=-1;
while(left<right){
mid=(left+right)/2;
if(arr[left]==left){
return left;
}
if(arr[right]==right){
res=right;
right=right-1;
continue;
}
if(arr[mid]==mid){
res=mid;
right=mid;
continue;
}
if(arr[mid]>mid){
right=mid-1;
continue;
}
if(arr[mid]<mid){
left=mid+1;
}
}
return res;
}
}
- 计算完全二叉树的节点数,要求低于O(N)
先计算层数,遍历到根节点左子树的最左节点,再确定根节点的右子树的最左节点能否到达这个层数
如果可以,则如下图所示,说明根节点的左子树是一棵满二叉树,可以计算根节点的左子树的个数加上根节点,然后递归这个过程计算右子树
如果不可以,则说明根节点的右子树一定是一棵满二叉树,可以计算右子树上的节点数,然后同样递归求左子树上节点个数
import java.util.*;
//完全二叉树节点数计算 递归求解节点数
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class CountNodes {
public int count(TreeNode root) {
if(root==null){
return 0;
}
return countNode(root);
}
//给一棵树的根节点,计算包括根节点在内的数的节点值多少
public int countNode(TreeNode root){
if(root==null){
return 0;
}
//遍历左子树的最左节点,确定以root为头结点的树的层数
//确定左右子树是否可以达到一个深度
int LDepth=getLayer(root.left);
int RDepth=getLayer(root.right);
int LNum,RNum;
if(LDepth==RDepth){
//如果左子树深度等于右子树深度 则左子树为完全二叉树 递归求右子树
LNum=(int)Math.pow(2,LDepth)-1;
RNum=countNode(root.right);
}else{
//如果右子树深度小于左子树深度 则右子树为完全二叉树 递归求左子树
RNum=(int)Math.pow(2,RDepth)-1;
LNum=countNode(root.left);
}
return LNum+RNum+1;
}
//求以root为根节点的树的层数
public int getLayer(TreeNode root){
if(root==null){
return 0;
}
TreeNode left=root.left;
int depth=1;
while(left!=null){
left=left.left;
depth++;
}
return depth;
}
}
- 用O(logN)方法求取K的N次方
为了防止溢出,请返回结果Mod 1000000007的值
第一种:递归
import java.util.*;
//用O(logN)的方法求解K的n次方
public class QuickPower {
public int getPower(int k, int N) {
if(N==0){
return 1;
}
if(N==1){
return k;
}
//根据奇偶决定
if(N%2==0){
long temp=getPower(k,N/2);
temp=(temp*temp)%1000000007;
return (int)temp;
}else{
long temp=getPower(k,(N-1)/2);
temp=(temp*temp)%1000000007;
return (int)((temp*k%1000000007));
}
}
}
第二种:利用二进制来决定是否要乘以当前这个次方数(也可以用数组保存每个次方结果,但是会占用额外的空间)
import java.util.*;
//用O(logN)的方法求解K的n次方
public class QuickPower {
public int getPower(int k, int n) {
long res=1,temp=k;
for(;n>0;n=n>>1){
if((n&1)!=0){
res=(res*temp)%1000000007;
}
temp=(temp*temp)%1000000007;
}
return (int)res%1000000007;
}
}