算法学习|二分查找

二分查找

  1. 二分查找的也称为折半查找,由于每次都能够将查找区间缩小为原来一半,这种算法的时间复杂度为O(logN)。

  2. 计算中值的方法有两种
    m = (low+ hight) / 2
    m = low + (hight - low) / 2
    推荐使用第二种,因为减法不会涉及到数据因为相加导致的溢出问题。

  3. 查找的返回值一般返回为low,但是实际情况需要从实际的题目出发。

  4. 可以多举例子,便于理解边界问题。

Question 1 

x 的平方根(https://leetcode-cn.com/problems/sqrtx/)(长按复制到浏览器即可到leetcode对应的题目)

算法的逻辑在具体的代码注释里

/**
 * 计算并返回 x 的平方根,其中 x 是非负整数。
 * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
 */
public class Question1 {
    public int mySqrt(int x) {
        if (x <= 1) {
            return x;
        }
//        二分
        int low = 1;
        int hight = x;
//        结合双指针
        while (low <= hight) {
//            定义中间值  这样定义middle不会出现溢出问题
            int middle = low + (hight - low) / 2;
//            计算是否能够直接算出开方
            int sqrt = x / middle;
            if (sqrt == middle) {
                return middle;
            } else if (middle > sqrt) {
//                大了 调整hight
                hight = middle - 1;
            } else {
//                小了 调整low
                low = middle + 1;
            }
        }
        return hight;
    }
}

Question 2 

寻找比目标字母大的最小字母(https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/)

/**
 * 给定一个只包含小写字母的有序数组letters 和一个目标字母 target,寻找有序数组里面比目标字母大的最小字母。
 * 数组里字母的顺序是循环的。举个例子,如果目标字母target = 'z' 并且有序数组为 letters = ['a', 'b'],则答案返回 'a'。
 * 关键词:有序、循环
 */
public class Question2 {
    public char nextGreatestLetter(char[] letters, char target) {
//        定义头尾
        int hight = letters.length-1;
        int low = 0;
        while (hight>=low){
            int middle = low + (hight-low)/2;
            if(target>=letters[middle]){
//                middle小了
                low = middle+1;
            }else {
//                middle大了
                hight = middle-1;
            }
        }
//        最终得到的low是接近结果的(因为只有low大于hight的时候才会跳出循环)
        return low<letters.length?letters[low]:letters[0];
    }
}

Question3

 有序数组中的单一元素(https://leetcode-cn.com/problems/single-element-in-a-sorted-array/)

/**
 * 给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
 * 注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
 * <p>
 * 因为要求时间复杂度和空间复杂度,所以不能够使用直接遍历的方法。
 */
public class Question3 {
    public int singleNonDuplicate(int[] nums) {
        int low = 0;
        int hight = nums.length - 1;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
//            判断中间的数是偶数还是奇数
            if (middle % 2 == 0) {
//                偶数  说明middle前面有偶数个数字 此时判断middle 和 middle+1的关系
                if (nums[middle] == nums[middle + 1]) {
//                    说明middle前面数字都是出现两次的
                    low = middle + 2;
                } else {
//                    说明middle后面的数字都是正确的
                    hight = middle;
                }
            } else {
//                奇数   说明前后都只有奇数个数字  判断middle 和middle+1的关系
                if (nums[middle] == nums[middle + 1]) {
//                   说明middle之后的都是正确的
                    hight = middle - 1;
                } else {
//                    说明middle+1之后的是有问题的
                    low = middle + 1;
                }
            }
        }
        return nums[low];
    }
}

Question4

第一个错误的版本(https://leetcode-cn.com/problems/first-bad-version/)

/**
 * 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。
 * 由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
 * <p>
 * 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
 * <p>
 * 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。
 * 实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
 */
public class Question4 {
    public int firstBadVersion(int n) {
        int low = 1;
        int hight = n;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
//            二分查找 提高效率
            if(isBadVersion(middle)){
//                版本出现错误 可能是在之前就出错了,所以需要往前回溯
                hight = middle;
            }else {
//                版本正确 往后推进
                low = middle+1;
            }
        }
        return low;
    }


    /**
     * leetcode系统提供的方法
     *
     * @param version
     * @return true:错误版本
     * false:正确版本
     */
//    boolean isBadVersion(int version) {
//        return false;
//   }
}

Question 5 

寻找旋转排序数组中的最小值(https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/)

/**
 * 旋转数组的最小数字问题
 * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
 * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
 * 请找出其中最小的元素。
 * 你可以假设数组中不存在重复元素。
 */
public class Question5 {
    public int findMin(int[] nums) {
        int low = 0;
        int hight = nums.length - 1;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
//            旋转数组的特征就是:发生旋转的话,就会有一个旋转点
//            我们的目标就是找到这个旋转点
//            (通过二分法比对middle和hight的值)
//            hight>middle的话 说明后半部分没哟旋转点 反之同理!
            if(nums[middle]<nums[hight]){
//                说明后半部分是正常的顺序的数组,没有旋转点,将目标缩小到前半部分
                hight = middle;
            }else {
//                说明后半部分数组有旋转点,继续调查后半部分的数组
                low = middle+1;
            }
        }
        return nums[low];
    }

Question 6 

在排序数组中查找元素的第一个和最后一个位置(https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)

/**
 * 题目描述:
 * 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
 * 你的算法时间复杂度必须是 O(log n) 级别。
 * 如果数组中不存在目标值,返回 [-1, -1]。
 * <p>
 * 关键词:升序数组、时间复杂度要求O(log n) 所以需要使用二分查找算法
 */
public class Question6 {
    public int[] searchRange(int[] nums, int target) {
//        寻找第一次出现target的地方
        int first = binarySearch(nums, target);
//        寻找第一次出现比target大1的数字的地方
        int last = binarySearch(nums, target + 1) - 1;
        if (first == nums.length || nums[first] != target) {
            return new int[]{-1, -1};
        } else {
            return new int[]{first, Math.max(first, last)};
        }
    }


    /**
     * 封装成方法 用于寻找target第一次出现的位置
     * @param nums
     * @param target
     * @return
     */
    private int binarySearch(int[] nums, int target) {
        int low = 0;
        int hight = nums.length;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
            if (nums[middle] >= target) {
//                说明目标在上半部分
                hight = middle;
            } else {
//                说明目标在下半部分
                low = middle + 1;
            }
        }
        return low;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据库采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方式,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据库。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值