Task 04:数组二分查找

如题所述

第1个回答  2022-07-17

第8-10天打卡,附上 学习链接

二分查找算法(Binary Search Algorithm),又称为折半查找、对数查找算法,是一种在有序数组中查找某一特定元素的搜索算法。
基本思想:先确定待查找元素所在的区间范围,再逐步缩小范围,直到找到或找不到该元素为止。

0704 二分查找 *:给定一个升序的数组nums和一个目标值target,返回target在数组中的位置,如果找不到,则返回-1。
样例1:输入为nums=[-1, 0, 3, 5, 9, 12],target=9,输出为4;
样例2:输入为nums=[-1, 0, 3, 5, 9, 12],target=2,输出为-1。

思路1:直接法。一旦在循环体中找到该需查找的元素,就直接返回结果。
该种思路适合解决简单题目,即查找的元素性质简单,数组中都是非重复元素,且等于不等于的情况易于比较。

思路2:排除法。在循环体中排除目标元素一定不存在的区间。
该思路适合于解决复杂题目,如查找一个数组里可能不存在的元素,找边界问题可使用该思路。

题目描述:每轮游戏,会从1到n随机选择一个数字;如果猜错了,会告诉是大了还是小了。可以通过调用一个预先定义好的接口int guess(int num)来获取猜测结果,返回值一共有3种可能的情况(-1,1或0),-1表示选出的数字比猜的数字小,即pick<num;1表示选出的数字比猜的数字大,即pick>num;0表示猜对了,即pick==num。返回我选出的数字。
样例1:输入为n=10,pick=6,输出为6;
样例2:输入为n=1, pick=1,输出为1;
样例3:输入为n=2,pick=1,输出为1;
样例4:输入为n=2,pick=2,输出为2。

解题思路:二分法。

题目描述:给定一个数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在数组中,返回它将会被按顺序插入的位置。
样例1:输入为nums=[1, 3, 5, 6],target=5,输出为2;
样例2:输入为nums=[1, 3, 5, 6],target=2,输出为1;
样例3:输入为nums=[1, 3, 5, 6],target=7,输出为4;
样例4:输入为nums=[1, 3, 5, 6],target=0,输出为0;
样例5:输入为nums=[1],target=0,输出为0。

解题思路:在一个有序数组中找到第一个大于等于target的下标。

题目描述:给一个非负整数x,计算并返回x的算术平方根。由于返回类型是整数,结果只保留整数部分,小数部分将被舍去。不允许使用任何内置指数函数和算符。 样例1:输入为x=4,输出为2; 样例2:输入为x=8,输出为2。解释:8的算术平方根是2.82842...,由于返回类型是整数,小数部分将被舍去。

解题思路:转化为寻找满足k 2 <=x的最大k值。二分查找,下界为0,上界粗略设定为x。每一步,通过比较中间元素mid的平方与x的大小关系,不断调整上下界的范围。

时间复杂度:O(log(x)); 空间复杂度:O(1)。

题目描述:给一个已按照 非递减顺序 排列的整数数组numbers,从数组中找出两个数满足相加之和等于目标数target。函数应该以长度为2的整数数组的形式返回这两个数的下标值。numbers的下标从1开始计数,所以答案数组应当满足1<=answer[0]<answer[1]<=numbers.length。可假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 样例1:输入为numbers=[2, 7, 11, 15],target=9,输出为[2, 7]; 样例2:输入为numbers=[2, 3, 4],target=6,输出为[1, 3]; 样例3:输入为numbers=[-1, 0],target=-1,输出为[1, 2]。

解题思路:固定第一个数,在其右侧寻找第二个数,使得第二个数等于目标值减去第一个数。(之前有做过一次)。

时间复杂度:O(nlog(n)); 空间复杂度:O(1)。

题目描述:传送带上的包裹必须在D天内从一个港口运送到另一个港口。传送带上的第i个包裹的重量为weights[i]。每一天,都会按照重量的顺序往传送带上装载包裹。装载的重量不会超过船的最大运载重量。返回能在D天内将传送带上的所有包裹送达的船的最低运载能力。 样例1:输入为weights=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],D=5,输出为15;解释:第1天是1,2,3,4,5;第2天是6,7;第3天是8;第4天是9;第5天是10,所以最低载重为15。 样例2:输入为weights=[3, 2, 2, 4, 1, 4],D=3,输出为6;解释:第1天是3,2;第2天是2,4;第3天是1,4;所以最低载重为6。 样例3:输入为weights=[1, 2, 3, 1, 1],D=4,输出为3。解释:第1天是1;第2天是2;第3天是3;第4天是1,1;所以最低载重是3。

解题思路: 二分查找转化为判定问题 。存在一个运载能力的下限x ans ,使得x>=x ans 时,可以在days天运送完,反之无法运送完所有包裹。因为有顺序运载,连续安排在同一天进行运送。当这批包裹的重量大于运载能力x时,就需要将最后一个包裹拿出来,安排在新的一天,并继续往下遍历。当遍历完整个数组后,就得到了最少需要运送的天数。使用 最少需要运送的天数 与days进行比较,小于等于days时,忽略二分的右半部分区间;当其大于days时,就忽略二分的左半部分区间。

时间复杂度:O(nlog(sum(w))); 空间复杂度:O(1)。

题目描述:每个版本的产品都是基于之前的版本开发,因此错误的版本之后的所有版本都是错误的。假设有n个版本[1, 2, ..., n],找出导致之后版本出错的第一个错误版本。可以调用bool isBadVersion(version)接口来判断版本号version是否在单元测试中出错。实现一个函数来查找第一个错误的版本。应该尽量减少对调用API的次数。 样例1:输入为n=5, bad=4,输出为4;解释:调用isBadVerison(3)->false;调用(4)->true;调用(5)->true。所以4是第一个错误的版本。 样子2:输入为n=1,bad=1,输出为1。

解题思路:左右初始化为1和n,从中间开始二分查找。

时间复杂度:O(log(n)); 空间复杂度:O(1)。

题目描述:整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标从0开始计数)。例如,[0, 1, 2, 4, 5, 6, 7]在下标3处经旋转后可能变为[4, 5, 6, 7, 0, 1, 2]。给出旋转后的数组nums和一个整数target,如果nums中存在这个目标值target,则返回它的下标,否则返回1。 样例1:输入为nums=[4, 5, 6, 7, 0, 1, 2],target=0,输出4; 样例2:输入为nums=[4, 5, 6, 7, 0, 1, 2],target=3,输出为-1; 样例3:输入为nums=[1],target=0,输出为-1。

解题思路:二分查找适用于有序数组。将数组从中间分开,肯定有一部分是有序的。首先mid分割,查看[left, mid]和[mid+1, right]哪个部分是有序的,根据有序的部分改变二分查找的上下界,根据有序的部分判断target在不在这个部分。

时间复杂度:O(log(n)); 空间复杂度:O(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,它原来是一个升序排列的数组,并进行了上述的多次旋转。找出并返回数组中的最小元素。 样例1:输入为nums=[3, 4, 5, 1, 2],输出为1; 样例2:输入为nums=[4, 5, 6, 7, 0, 1, 2],输出为0; 样例3:输入为nums=[11, 13, 15, 17],输出为11。

解题思路:考虑数组中的最后一个元素x,在最小值右侧的元素,值一定都严格小于x;左侧一定都严格大于x。二分查找,左边界为low,右边界为high,区间的中点为pivot,最小值在该区间内。将中点元素与右边界元素相比。 (1)中点元素<右边界元素,则中点在最小值的右侧,可以忽略二分查找的右半部分; (2)中点元素>右边界元素,则中点在最小值的左侧,可以忽略二分查找的左半部分; 因为数组不包含重复元素,且当前的长度不为1,则不会和high重合。如果长度为1,可以结束二分查找。

时间复杂度:O(log(n)); 空间复杂度:O(1)。

相似回答