算法与数据结构-二分搜索 讲解与java代码实现

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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值