算法通关村第九关——二分查找白银挑战笔记

本篇笔记在于进一步巩固二分查找递归模板和非递归模板,同时扩充二分查找的变式问题,从而深层次的掌握二分查找的边界条件,形成针对变式问题自主分析边界条件,灵活运用模板解决问题!同时,该部分将树和二分查找相结合,引出二叉排序树概念,并对判断二叉排序树与中序遍历知识点相结合,温故知新!

1.二分查找变式问题

1.1山脉数组的封顶索引

题目见LeetCode852,其描述为在一个长度大于3的数组中,前半部分存储的元素递增,后半部分存储的元素递减,求该数组的最大元素(峰),其中峰的位置不在数组的左右两端。

题目分析:对于数组的前半段,一定满足arr[i] < arr[i+1],后半段一定满足arr[i] > arr[i+1]。当找到第一个发生变化的arr[i]就是数组的峰。厘清思路,直接上代码!

    public static int myPeakIndexInMountainArray(int[] arr) {
        int n = arr.length;
        int index = -1;
        for (int i = 0; i < n - 1; i++) {
            if(arr[i] > arr[i + 1]) {
                index = i;
                break;
            }
        }
        return index == -1 ? n - 1 : index;
    }

是的,这样的方法可以解决,但是时间复杂度是O(n),能有时间复杂度更低的方法吗?答:二分查找,时间复杂度为O(logn).

二分查找,模板是第一,边界条件是关键!那么边界条件是什么呢?

考虑中间mid节点,自然分成三种情况,找到峰,峰左侧和峰右侧。

找到峰:arr[mid]>arr[mid-1] && arr[mid]>arr[mid+1];

峰左侧:arr[mid-1]<arr[mid] && arr[mid]<arr[mid+1];

峰右侧:arr[mid-1]>arr[mid] && arr[mid]>arr[mid-1];

当找到峰的时候,返回mid;当在峰左侧的时候,low=mid+1;当在峰右侧的时候,high=low+1;

厘清逻辑,直接上代码!

    public static int myPeakIndexInMountainArray2(int[] arr){
        int low = 0;
        int high = arr.length-1;
        while(low < high){
            int mid = low +((high - low) >> 1);
            if(arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) return mid;
            if(arr[mid] > arr[mid - 1] && arr[mid] < arr[mid + 1]) low = mid + 1;
            if(arr[mid] < arr[mid - 1] && arr[mid] > arr[mid + 1]) high = mid - 1;
        }
        return low;
    }

1.2旋转数字的最小数字

题目见LeetCode153

什么是旋转数字?在之前的链表和数组的旋转题目中提及过,将数组(链表)向一个方向旋转即可得到旋转数组。

那么,旋转数组有什么特性?答:以最小元素为分界点,最小元素左侧各元素依次递增,最小元素右侧各元素也是依次递增,左侧元素整体大于右侧元素,如图(来自鱼骨头哥):

 那么,我们就需要利用这个特性来找最小元素!也就是二分查找的边界条件

比较mid和high的大小,当mid<high说明,mid到high之间的元素全部在最小元素的右侧,那么下一查找,让high=mid(因为可能此时mid就是最小元素);当mid>high说明,low到high之间的元素全部在最小元素的的左侧,那么下一次查找,让low=mid+1。厘清逻辑,直接上代码!

    public static int myFindMin(int[] nums) {
        int low = 0;
        int high = nums.length -1;
        while(low < high){
            int mid = low + ((high - low) >> 1);
            if(nums[mid] == nums[high]) return nums[mid];
            else if(nums[mid] < nums[high]) high = mid;
            else low = mid + 1;
        }
        return nums[low];
    }

 1.3找缺失数字

题目见剑指 Offer 53Ⅱ ,描述为找到一个从0开始的连续序列中的缺失数字。

如何二分查找?答:看二分查找边界条件

假如序列从i位置开始缺失,那么0到i-1位置均满足nums[i] = i;i到n均满足nums[i]!=i。那么,我们找到第一个nums[i]!=i的元素,或者找到最后一个nums[i]=i的元素的下一个位置,就可以找到最终缺失的元素!这里有巨坑!!!我们采用方式二!

找到中间mid节点,当nums[mid]!=mid说明,mid右侧元素全部在缺失数字的右侧,应该在左侧查找缺失数字,因此high=mid-1;当nums[mid]==mid说明,mid左侧元素全部在缺失数字的左侧,应该在右侧查询缺失数字,因此low=mid+1。理清逻辑,直接上代码!

    public static int mySolve2(int[] a) {
        int low = 0;
        int high = a.length - 1;
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            //左半部分在缺失数字之前
            if(a[mid] == mid) low = mid + 1;
                //右半部分在缺失数字之后
            else high = mid - 1;
        }
        return low;
    }

1.4优化求平方根

题目见剑指Offer,描述为最快方式找x的平方根(四舍五入),怎么做?

边界条件:mid * mid == x,找到平方根;mid * mid < x,右侧找平方根;mid * mid > x,左侧找平方根。厘清思路,直接上代码!

    public static int mySqrt(int x) {
        int low = 1, high = x;
        while(low < high){
            int mid = low + ((high - low) >> 1);
            if(mid * mid == x) return mid;
            else if (mid * mid < x) low = mid + 1;
            else high = mid - 1;
        }
        return low;
    }

2.二叉排序树与中序遍历

2.1二叉排序树搜索特定值

题目见LeetCode700。

很简单,直接上代码!(递归和迭代两种实现)

    public static TreeNode mySearchBST(TreeNode root, int val) {
        if(root == null || root.val == val) return root;
        return val < root.val ? mySearchBST(root.left, val) : mySearchBST(root.right, val);
    }

    public static TreeNode mySearchBST2(TreeNode root, int val) {
        while(root != null && root.val != val){
            root = val < root.val ? root.left : root.right;
        }
        return root;
    }

2.2验证二叉排序树

如何验证二叉排序树?依据二叉排序树的特性,中序遍历序列必然从小到大顺序排列,可以先验证左子树是否是二叉排序树,在验证当前节点是否大于左子树中最后遍历的节点,在验证右子树是否是二叉排序树即可!厘清逻辑,直接上代码!

    long pre = Long.MIN_VALUE;

    public static boolean myIsValidBST(TreeNode root) {
        if(root == null) return true;
        if(!myIsValidBST(root.left)) return false;
        if(pre >= root.val) return false;
        pre = root.val;
        return myIsValidBST(root.right);
    }

OK,《算法通关村第九关——二分查找白银挑战笔记》结束,喜欢的朋友三联加关注!关注鱼市带给你不一样的算法小感悟!(幻听)

再次,感谢鱼骨头教官的学习路线!鱼皮的宣传!小y的陪伴!ok,拜拜,第九关第三幕见!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值