文章目录
- 前言
- 一、二分查找
- 二、数组完全有序且不重复
-
- 三、数组完全有序且重复
-
- 四、数组部分有序且不重复
-
- 五、数组部分有序且重复
-
- 六、二维数组
- 总结
前言
刷题时对于二分查找法的一些总结
提示:以下是本篇文章正文内容,下面案例可供参考
一、二分查找
二分查找也称折半查找(Binary Search),是一种在有序数组中查找某一特定元素的搜索算法。我们可以从定义可知,运用二分搜索的前提是数组必须是有序的,这里需要注意的是,我们的输入不一定是数组,也可以是数组中某一区间的起始位置和终止位置
二、数组完全有序且不重复
1.第一题
题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
int search(vector<int>&nums,int target)
{
if(nums.size()==0)
{
return -1;
}
int left=0;
int right==nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(nums[mid]==target)
{
return mid;
}
else if(target<nums[mid])
{
right=mid-1;
}
else
{
left=mid+1;
}
}
return -1;
}
2.第二题
题目描述:给定一个排序的整数数组 nums 和一个整数目标值 target ,请在数组中找到 target ,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 :
输入: nums = [1,3,5,6], target = 5
输出: 2
输入: nums = [1,3,5,6], target = 2
输出: 1
int searchInsert(vector<int>& nums, int target)
{
if(nums.size()==0)
{
return 0;
}
int left=0;int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(target<=nums[mid])
{
right=mid-1;
}
else
{
left=mid+1;
}
}
return left;
}
三、数组完全有序且重复
1.第一题
题目描述:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
if(nums.size()==0)
{
return {-1,-1};
}
else if(FindFirstPos(nums,target)<FindLastPos(nums,target))
{
return {-1,-1};
}
else
{
return {FindFirstPos(nums,target),FindLastPos(nums,target)};
}
}
private:
int FindFirstPos(vector<int>& nums, int target)
{
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(target<=nums[mid])
{
right=mid-1;
}
else if(target>nums[mid])
{
left=mid+1;
}
}
return left;
}
int FindLastPos(vector<int>& nums, int target)
{
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(target<nums[mid])
{
right=mid-1;
}
else if(target>=nums[mid])
{
left=mid+1;
}
}
return right;
}
};
2.第二题
题目描述:从数组或区间中找出第一个大于或最后一个小于目标元素的数的索引,例 nums = {1,3,5,5,6,6,8,9,11} 我们希望找出第一个大于 5的元素的索引,那我们需要返回 4 ,因为 5 的后面为 6,第一个 6 的索引为 4,如果希望找出最后一个小于 6 的元素,那我们则会返回 3 ,因为 6 的前面为 5 最后一个 5 的索引为 3。
输入:{1,3,5,5,6,6,8,9,11} target=5
输出:4
解释:比5大的第一个数是6,输出下标4;
int firstBigNum(vector<int>& nums, int target)
{
if(nums.size()==0)
{
return -1;
}
int left=0;int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(target<nums[mid])
{
if(mid==0||target>=nums[mid-1])
{
return mid;
}
else
{
right=mid-1;
}
}
if(target>=nums[mid])
{
left=mid+1;
}
}
return -1;
}
int lastSmallNum(vector<int>& nums, int target)
{
if(nums.size()==0)
{
return -1;
}
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(target<=nums[mid])
{
right=mid-1;
}
else if(target>nums[mid])
{
if(mid==nums.size()-1||target<=nums[mid+1])
{
return mid;
}
else
{
left=mid+1;
}
}
}
return -1;
}
四、数组部分有序且不重复
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 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 :
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
int search(vector<int>& nums, int target)
{
if(nums.size()==0)
{
return -1;
}
if(nums.size()==1)
{
return target==nums[0]?0:-1;
}
int left=0;int right=nums.size()-1;
while(left<=right)
{
if(target==nums[mid])
{
return mid;
}
else if(nums[left]<=nums[mid])
{
if(nums[left]<=target&&target<nums[mid])
{
right=mid-1;
}
else
{
left=mid+1;
}
}
else
{
if(nums[mid]<target&&target<=nums[right])
{
left=mid+1;
}
else
{
right=mid-1;
}
}
}
return -1;
}
2.第二题
题目描述:已知一个长度为 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 ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
int findMin(vector<int>& nums)
{
if(nums.size()==1)
{
return nums[0];
}
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(nums[mid]>=nums[right])
{
left=mid+1;
}
else
{
right=mid;
}
}
return nums[right];
}
五、数组部分有序且重复
1.第一题
题目描述:假设按照升序排序的数组在预先未知的某个点上进行了旋转。(与第四部分第一题对比)
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例
输入:nums = [1,3,1,1,1], target = 3 输出:true
示例 2:
输入:nums = [2,5,6,0,0,1,2], target = 3 输出:false
bool search(vector<int>& nums, int target)
{
if(nums.size()==0)
{
return false;
}
if(nums.size()==1)
{
return nums[0]==target?true:false;
}
int right=nums.size()-1;
int left=0;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(nums[mid]==target)
{
return true;
}
else if(nums[left]==nums[mid])
{
++left;
continue;
}
else if(nums[left]<nums[mid])
{
if(nums[left]<=target&&target<nums[mid])
{
right=mid-1;
}
else
{
left=mid+1;
}
}
else
{
if(nums[mid]<target&&target<=nums[right])
{
left=mid+1;
}
else
{
right=mid-1;
}
}
}
return false;
}
2.第二题
题目描述:(与第四部分第二题对比)已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,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 = [1,3,5]
输出:1
示例 2:
输入:nums = [2,2,2,0,1]
输出:0
int findMin(vector<int>& nums)
{
if(nums.size()==1)
{
return nums[0];
}
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=left+((right-left)>>1);
if(nums[mid]==nums[right])
{
--right;
}
else if(nums[mid]>nums[right])
{
left=mid+1;
}
else
{
right=mid;
}
}
return nums[left];
}
3.第三题
搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。
示例1:
输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5
输出: 8(元素5在该数组中的索引)
示例2:
输入:arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 11
输出:-1 (没有找到)
int search(vector<int>& arr, int target)
{
int n=arr.size();
if(n==0) return -1;
else if(n==1)
{
if(arr[0]==target)
{
return 0;
}
return -1;
}
int left=0,right=n-1;
while(left<=right)
{
if(arr[left]==target)
{
return left;
}
int mid=left+(right-left)/2;
if(arr[mid]==target)
{
right=mid;
}
else if(arr[left]==arr[mid])
{
left++;
continue;
}
else if(arr[left]<arr[mid])
{
if(arr[left]<=target&&target<arr[mid])
{
right=mid-1;
}
else
{
left=mid+1;
}
}
else
{
if(arr[mid]<target&&target<=arr[right])
{
left=mid+1;
}
else
{
right=mid-1;
}
}
}
return -1;
}
六、二维数组
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1 \le n \le 100001≤n≤10000,数组中任意元素的值: 0 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)
示例1
输入:
[3,4,5,1,2]
复制
返回值:
1
bool myfind(int target,vector<int>&nums)
{
int left=0,right=nums.size()-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]==target)
{
return true;
}
if(nums[mid]<target)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
return false;
}
bool Find(int target, vector<vector<int> > array)
{
int flag=false;
if(array.size()==0) return flag;
int n=array.size();
for(int i=0;i<n;++i)
{
int tag=myfind(target, array[i]);
if(tag)
{
flag=true;
break;
}
}
return flag;
}
bool Find(int target, vector<vector<int> > array)
{
int m=array.size();
int n=array[0].size();
int row=0;
int col=n-1;
while(row<m&&col>=0)
{
if(array[row][col]==target)
{
return true;
}
else if(array[row][col]<target)
{
++row;
}
else
{
--col;
}
}
return false;
}
总结
二分查找真他妈的是个细节怪!!!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)