面试中的查找算法有顺序查找、二分查找、哈希表查找以及二叉树查找。
哈希表查找通常用在字符串中的字符查找O(1),如:查找字符串中第一个只出现一次的字符。
如果说给定的序列是已经排序(或者是已排序的变种),那么大概率要使用二分查找。
(1)最左查找:给定一个有序(非降序)数组A,可含有重复元素,求最小的i使得A[i]等于target
这个题的直观思路是先找到一个target然后向左遍历,但是这样最坏的时间复杂度可能是O(n);可以使用变形二分搜索
int BinarySearch(vector<int> nums, int target)
{
if(nums.size() <= 0)return -1;
int start = 0, end = nums.size()-1;
while(start < end){
int mid = start + (end - start)/2;
if(nums.at(mid) < target)
start = mid + 1;
else
end = mid; //在中间节点值大于或者等于时,将end更新为中间节点索引
}
if(nums.at(start) != target)return -1;
return start;
}
同理还有最右查找也可以使用这种变形的二分搜索
变形:数字在排序数组中出现的次数(剑指offer38)
可以通过求出最左和最右的target得到最终的答案:
int GetFirst(int* data,int start,int end,int k)
{
if(start>end)
return -1;
int middleIndex= (start+end)/2;
int middleDate= data[middleIndex];
if(middleDate==k)
{
if(middleIndex==start)
return middleIndex;
else if(data[middleIndex-1]!=k)
return middleIndex;
else
end=middleIndex-1;
}
else if(middleDate>k)
end=middleIndex-1;
else
start=middleIndex+1;
return GetFirst(data,start,end,k);
}
int GetLast(int* data,int start,int end,int k)
{
if(start>end)
return -1;
int middleIndex=(end+start)/2;
int middleDate = data[middleIndex];
if(middleDate==k)
{
if(middleIndex==end)
return middleIndex;
else if(data[middleIndex+1]!=k)
return middleIndex;
else
start = middleIndex+1;
}
else if(middleDate>k)
end=middleIndex-1;
else
start=middleIndex+1;
return GetLast(data,start,end,k);
}
int GetNum(int* data,int length,int k)
{
int first=GetFirst(data,0,length-1,k);
int last =GetLast(data,0,length-1,k);
if(first==-1 || last==-1)
return 0;
return last-first+1;
}
(2)旋转数组的最小数字( 剑指offer8)
1,2,3,4,5 --->3,4,5,1,2称为排序数组的旋转
最小数字刚好为两个子数组的分界线
同样可以采用二分搜索的方法,但是需要考虑到数组中可能有重复的数字,即nums.at(index1) == nums.at(index2) == nums.at(mid)这种情况就必须使用顺序搜索
int find2(int ar[],int index1,int index2)
{
int result=ar[index1];
for(int i=index1;i<=index2;i++)
{
if (ar[i]<result)
result =ar[i];
}
return result;
}
int find_min(int ar[],int length)
{
if (ar==NULL||length<=0)
return -1;
int index1=0;
int index2=length-1;
int index_mid;
if (ar[index1]<ar[index2])//本来就是递增
return ar[index1];
while(ar[index1]>=ar[index2])
{
if (index2-index1==1)
{
return ar[index2];
}
index_mid=(index1+index2)/2;
//若三者都相同无法判断,只能采用顺序查找
if(ar[index_mid]==ar[index1] && ar[index_mid]==ar[index2])
return find2(ar,index1,index2);
if(ar[index_mid]>=ar[index1])
index1=index_mid;
if(ar[index_mid]<=ar[index2])
index2=index_mid;
}
}
变形:
--------旋转数组中的查找(无重复)(leetcode33)
int search(vector<int>& nums, int target) {
if(nums.size() <= 0)return -1;
int start = 0;
int end = nums.size() - 1;
while(start <= end){
int mid = (start + end)/2;
if(nums.at(mid) == target)return mid;
//左半区间有序
if(nums.at(mid) >= nums.at(start)){
if(nums.at(mid) >= target && nums.at(start) <= target)end = mid-1;
else start = mid + 1;
}
//右半区间有序
else{
if(nums.at(mid) <= target && nums.at(end) >= target)start = mid+1;
else end = mid -1;
}
}
return -1;
}