[leetcode刷题]——二分查找
此篇博客主要记录力扣中二分查找的题。
一、求开方
69. x 的平方根
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
public int mySqrt(int x) { if(x <= 1) return x; int l = 0; int h = x; while(h >= l){ int mid = (l + h) / 2; if(mid > x /mid){ //如果使用 mid * mid > x ,则可能会内存溢出 h = mid - 1; }else if(mid < x / mid){ l = mid + 1; }else{ return mid; } } return h; }
二、大于给定元素的最小元素
744.寻找比目标字母大的最小字母 easy 2021-06-13
给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。
在比较时,字母是依序循环出现的。举个例子:
如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'
这个题考察的是二分,题目中是有序数组,我却使用了遍历。耗时一毫秒, 超过5%的用户。
public char nextGreatestLetter(char[] letters, char target) { int minLetter = 0; for(int i = 0; i < letters.length; i++){ if(subLetter(letters[i], target) < subLetter(letters[minLetter], target)){ minLetter = i; } } return letters[minLetter]; } public int subLetter(char a, char b){ if(a - b > 0){ return a - b; }else{ return a - b + 26; } }
利用有序的性质,使用二分,耗时超过100%😊
public char nextGreatestLetter(char[] letters, char target) { int l = 0; int h = letters.length - 1; while(l <= h){ int m = l + (h - l) / 2; if(letters[m] <= target){ l = m + 1; }else{ h = m - 1; } } return l < letters.length ? letters[l] : letters[0]; }
三、有序数组的Single Element
540.有序数组中的单一元素 medium 2021-06-13
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
public int singleNonDuplicate(int[] nums) { int l = 0; int h = nums.length - 1; while(l < h){ int m = l + (h - l) / 2; if((m - l) % 2 == 0){ //两侧都是偶数个 if(nums[m] == nums[m - 1]){ h = m - 2; }else if(nums[m] == nums[m + 1]){ l = m + 2; }else{ return nums[m]; } }else{ //两侧都是奇数个 if(nums[m] == nums[m - 1]){ l = m + 1; }else if(nums[m] == nums[m + 1]){ h = m - 1; }else{ return nums[m]; } } } return nums[l]; }
四、第一个错误的版本
278. 第一个错误的版本 easy 2021-06-13
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数
public int firstBadVersion(int n) { int l = 1; int h = n; while(l < h){ int m = l + (h - l) / 2; if(isBadVersion(m) == true){ h = m - 1; }else{ l = m + 1; } } if(isBadVersion(l) == true){ return l; }else{ return l + 1; } }
我写的代码确实呆板,二分法需要仔细审题。标准解法更简洁
public int firstBadVersion(int n) { int l = 1, h = n; while (l < h) { int mid = l + (h - l) / 2; if (isBadVersion(mid)) { h = mid; } else { l = mid + 1; } } return l; }
五、旋转数组的最小数字
153.寻找旋转排序数组中的最小值 medium 2021-06-13
已知一个长度为 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 ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
虽然程序运行正确,用时击败100%, 内存击败92%。但是总感觉程序不太优美。
public int findMin(int[] nums) { int len = nums.length; if(nums[0] <= nums[len - 1]){ return nums[0]; } int l = 0; int h = len - 1; while(l < h){ int m = l + (h - l) / 2; if(nums[m] > nums[l]){ l = m; }else{ h = m; } } return nums[h + 1]; }
别人的做法,时间和空间消耗差不多,但是更简洁更清晰。
public int findMin(int[] nums) { int l = 0, h = nums.length - 1; while (l < h) { int m = l + (h - l) / 2; if (nums[m] <= nums[h]) { h = m; } else { l = m + 1; } } return nums[l]; }
六、查找区间
34. 在排序算法数组中查找元素的第一个和最后一个位置 medium 2021-06-13
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
明知是个二分的题,但是却想不到二分的办法解决它。
public int[] searchRange(int[] nums, int target) { int[] res = {-1, -1}; for(int i = 0; i < nums.length; i++){ if(res[0] == -1 && nums[i] != target){ continue; }else if(res[0] == -1 && nums[i] == target){ res[0] = i; res[1] = i; }else if(res[0] != -1 && nums[i] == target){ res[1] = i; }else{ continue; } } return res; }
正确的的做法,可惜不是我想出来的😭
public int[] searchRange(int[] nums, int target) { int start = findFirst(nums, target); int end = findFirst(nums, target + 1) - 1; if(start == nums.length || nums[start] != target){ return new int[]{-1, -1}; }else{ return new int[]{start, end}; } } public int findFirst(int[] nums, int target){ int l = 0; int h = nums.length; //这里并不是nums.length - 1。 因为我们需要更大的范围去找end. while(l < h){ int m = l + (h - l) / 2; if(nums[m] >= target){ h = m; }else{ l = m + 1; } } return h; }

浙公网安备 33010602011771号