第一道:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
来源:力扣(LeetCode)
刚开始,我尝试的第一种做法,利用最简单的直接遍历查找,查找到了,就返回下标,没有查找到,就返回-1,称为顺序查找。
class Solution {
public int search(int nums[],int target){
for(int i = 0;i<nums.length;i++){
if(nums[i]==target){
return i;
}
}
return -1;
}
}
然后,我看了以下官方给出的第二种做法的思路,然后利用学过的二分查找的基本知识,给了左右两个指针,目标比以左为下标的数组值大的,左指针++,反之,则右指针--,但是后来我发现我用的不是二分查找,我这种做法实际上还是顺序查找,只是我把数组变成了一个做指针,至于右指针根本用不到,因为升序条件本来就符合,况且我用的是else if,但是基于这种做法,我想是不是可以左右指针移动,两者相等的时候其实就是目标值呢,于是我又试了一遍,但是发现没有必要,当左右指针相等的时候,左指针已经指到目标值那里了,总结下来就是我用了一种很冒牌的二分查找。
class Solution {
public int search(int nums[],int target){
int left = 0;
int right = nums.length-1;
while(right>=left){
if(nums[left]==target){
return left;
}else if(nums[right]==target){
return right;
}else if(target>=nums[left]){
left++;
}else if(target<nums[right]){
right--;
}
}
return -1;
}
}
我又看了一遍官方给出的代码块,第三种做法,这才是正宗的二分查找,我的记忆又恢复了,遇到题目中出现“有序的(升序)整型数组”,这个时候就应该注意一下是不是会用到二分查找。
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (right - left) / 2 + left;
int num = nums[mid];
if (num == target) {
return mid;
} else if (num > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
第二道:
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/first-bad-version
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题目,对于我来说初次做的时候,第一点可能以为这个接口要我自己写还是怎么样,导致比较迷糊。第二点就是思路很混乱,不知道怎么去下手。 之后我发现,我上一题的多此一举,其实就是这一道题的方法,虽然“当左右指针相等的时候,左指针已经指到目标值那里了”,但是对于这道题而言,指针相等的时候,恰恰就是找到错误之处。所以当不很清楚while里面的等号的时候你可以先举一个一般情况,然后再举一个两个数字的极端情况,这样问题就迎刃而解了。
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1, right = n;
while (left < right) { // 循环直至区间左右端点相同
int mid = left + (right - left) / 2; // 防止计算时溢出
if (isBadVersion(mid)) {
right = mid; // 答案在区间 [left, mid] 中
} else {
left = mid + 1; // 答案在区间 [mid+1, right] 中
}
}
// 此时有 left == right,区间缩为一个点,即为答案
return left;
}
}
第三道:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-insert-position
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
int mid = 0;
while(left<=right){
mid = (right+left)/2;
int num = nums[mid];
if(target==num){
return mid;
}
if(target>num){
left = mid+1;
}else {
right = mid-1;
}
}
if(target>nums[mid]){
return mid+1;
}else {
return mid-1;
}
}
}
第一次,虽然思路应该没有问题,但是,最后一步返回插入位置的时候,可能对于时间复杂度上不满足了,看了题解之后,发现是这样子。
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
}
经过验证,的确如此。
今天的题目做完了,不知道是不是为做题开了一个好头,只是感觉到每一个算法的本身,其实理解不难,但是灵活运用起来,还是有一定差距,果然是每一步都有其特定应用的场景,说白了,就是,你要对这个算法的每一个过程非常的了解,然后再根据题目的特定场景对照思路和某一步,是否逻辑一致。
今天的练习就到这里了,我希望今天一天之内,可以将这些好好消化掉,做一个不再是搬运代码,没有灵魂的人,多思考,一题多解,提高效率,才是正道。