基于二分查找的扩展问题
山脉数组的峰顶索引
山脉数组的峰顶索引 符合下列属性的数组 arr 称为 山脉数组 :
-
arr.length >= 3
-
存在 i(0 < i < arr.length - 1)使得:
- arr[0] < arr[1] < ... arr[i-1] < arr[i]
-
- arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给你由整数组成的山脉数组 arr ,返回满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1] 的下标 i 。 你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。
示例 1: 输入:arr = [0,1,0] 输出:1
示例 2: 输入:arr = [0,2,1,0] 输出:1
示例 3: 输入:arr = [0,10,5,2] 输出:1
class Solution {
public int peakIndexInMountainArray(int[] arr) {
int l=0;
int r=arr.length-1;
while (l<=r){
int m=l+((r-l)>>1);
if(arr[m]>arr[m+1]&&arr[m]>arr[m-1]){
return m;
}else if(arr[m]>arr[m+1]&&arr[m-1]>arr[m]){
r=m-1;
}else {
l=m+1;
}
}
return -1;
}
}
旋转数字的最小数字
旋转数字的最小数字 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
-
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
-
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。 给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1: 输入:nums = [3,4,5,1,2] 输出:1 解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2: 输入:nums = [4,5,6,7,0,1,2] 输出:0 解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
示例 3: 输入:nums = [11,13,15,17] 输出:11 解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
class Solution {
public int findMin(int[] nums) {
int l=0;
int r=nums.length-1;
while (l<r){
int m=l+((r-l)>>1);
if(nums[m]<nums[r]){
r=m;
}else {
l=m+1;
}
}
return nums[l];
}
}
找缺失的数据
:::info
剑指offr题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0n-1
之内。在范围0n-1内的个数字中有且只有一个数字不在该数组中,请找出这个数字。
这个题很简单是不?从头到尾遍历一遍即可确定,但是这么简单肯定不是面试需要的。那这个题要考什么
呢?就是二分查找。
对于有序的也可以用二分查找,这里的关键点是在缺失的数字之前,必然有ums[i]==i,在缺失的数字之
后,必然有nums!=i。
因此,只需要二分找出第一个ums[i]!=i,此时下标就是答案。若数组元素中没有找到此下标,那么缺失
的就是n。代码如下:
:::
class Solution {
public static void main(String[] args) {
int[] nums=new int[]{0,1,2,3,5,6};
System.out.println(missingNumber(nums));
}
public static int missingNumber(int[] nums) {
int l=0;
int r=nums.length-1;
while (l<r){
int m=l+((r-l)>>1);
if(nums[m]==m){
l=m+1;
}else {
r=m;
}
}
return l;
}
}
优化平方根
x的平方根 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。 由于返回类型是整数,结果只保留 **整数部分 **,小数部分将被 舍去 。 **注意:**不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1: 输入:x = 4 输出:2
示例 2: 输入:x = 8 输出:2 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去
class Solution {
public static void main(String[] args) {
mySqrt(8);
}
public static int mySqrt(int x) {
if(x==0) return 0;
int l=1;
int r=x;
while (l<=r){
int m=l+((r-l)>>1);
if(x/m>m){
l=m+1;
}else if(x/m<m&&x/(m-1)>m-1){
return m-1;
}else if(x/m<m){
r=m-1;
}else if(x/m==m) {
return m;
}
}
return l;
}
}
中序与搜索树原理
:::info
在前面我们发现很多题使用前序、后序或者层次遍历都可以解决,但几乎没有中序遍历的。这是因为中序
与前后序相比有不一样的特征,例如中序可以和搜索树结合在一起,但是前后序则不行。
二叉搜索树是一个很简单的概念,但是想说清楚却不太容易。简单来说就是如果一棵二叉树是搜索树,则
按照中序遍历其序列正好是一个递增序列。比较规范的定义是:
·若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值,
·若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值,
·它的左、右子树也分别为二叉排序树。下面这两棵树一个中序序列是{3,6,9,10,14,16,19},一个是
{3,6,9,10},因比都是搜索树:
:::
二叉搜索树中搜索特定值
二叉搜索树中搜索特定值 给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
示例 1:
输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]
示例 2:
输入:root = [4,2,7,1,3], val = 5
输出:[]
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root.val==val) {
return root;
}else if(root.val>val){
if(root.left==null) return null;
return searchBST(root.left,val);
}else {
if(root.right==null) return null;
return searchBST(root.right,val);
}
}
}
验证二叉搜索树
验证二叉搜索树 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下:
-
节点的左子树只包含** 小于 **当前节点的数。
-
节点的右子树只包含 大于 当前节点的数。
-
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
class Solution {
long pre=Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root==null) return true;
if(!isValidBST(root.left)){
return false;
}
if(root.val<=pre){
return false;
}
pre=root.val;
return isValidBST(root.right);
}
}