查找包含,顺序查找,二分查找,哈希表查找和二叉排序树查找。
一. 二分查找
题目:旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
思路
重点说一下二分查找法。如果是在排序的数组(或者部分排序的数组)中查找一个数字或者统计某个数字出现的次数,那么都可以尝试二分查找算法。二分查找法的精髓就在于,两个指针,一个high,一个low,通过mid来比较,然后将mid赋给high或者low。
代码
#include<iostream>
using namespace std;
int findMin(int*array,int length)
{
int p1=0;
int p2=length-1;
int pmid=0;
while(p1<p2)
{
if(array[p1]<array[p2])
return array[p1];//旋转了0个数字,也就是说,是数组本身
if(p2-p1==1)
return array[pmid];
pmid=((p2-p1)>>1)+p1;//特别注意,pmid=(p2-p1)>>1+p1;少一个括号是完全不一样的,或者直接写pmid=(p1+p2)/2
if(array[pmid]>array[p1])
p1=pmid;
else if(array[pmid]<array[p2])
p2=pmid;
}
}
int main()
{
int array[5]={3,4,5,1,2};
cout<<findMin(array,5);
}
补充位运算
int main()
{
int a=2,b=4;
int c=((a+b)>>1)+1;
int d=(a+b)>>1+1;
cout<<c<<" "<<d<<endl;
}
上述代码,c为4,d为1.
题目:统计一个数字num在排序数组中出现的次数。
一般解法:用二分查找法去找到那个数字,然后往前往后遍历统计次数,但这种方法可能需要遍历n个元素,因此时间复杂度是O(n)。
#include<iostream>
using namespace std;
int FindNum(int*arr,int length,int num)
{
int low=0;
int high=length-1;
int count=0;
int mid=(high+low)/2;
while(high>=low&&arr[mid]!=num)
{
mid=(high+low)/2;
if(arr[mid]<num)
low=mid;
else if(arr[mid]>num)
high=mid;
}
if(high>=low&&arr[mid]==num)
{
count=1;
int temp=mid;
while(temp-->=0)
{
if(arr[temp]==num)
count++;
}
temp=mid;
while(temp++<=length-1)
{
if(arr[temp]==num)
count++;
}
}
return count;
}
int main()
{
int arr[9]={1,3,3,3,3,5,5,5,5};
int res=FindNum(arr,9,5);
cout<<res;
}
高级解法:上面那个解法慢是因为要去确定num第一次和最后一次出现的位置,因此就算使用了二分查找法还是很慢。但可以用二分查找法去确定num第一次出现的位置。用二分查找法找到一个num之后,看num前面的数是否为num,如果不是,则这个num就是第一个,否则第一个num就在前半段,二分了。
#include<iostream>
using namespace std;
int FindFirstNum(int*arr,int length,int num)
{
int low=0;
int high=length-1;
int count=0;
int mid=(high+low)/2;
while(high>=low)
{
mid=(high+low)/2;
if(arr[mid]<num)
low=mid;
else if(arr[mid]>num)
high=mid;
else if(arr[mid]==num)
{
if(arr[mid-1]!=num)
return mid;
else high=mid;
}
}
}
int FindLastNum(int*arr,int length,int num)
{
int low=0;
int high=length-1;
int count=0;
int mid=(high+low)/2;
while(high>=low)
{
mid=(high+low)/2;
if(arr[mid]<num)
low=mid;
else if(arr[mid]>num)
high=mid;
else if(arr[mid]==num)
{
if(arr[mid+1]!=num)
return mid;
else low=mid;
}
}
}
int main()
{
int arr[9]={1,3,3,3,3,5,5,5,5};
int first=FindFirstNum(arr,9,3);
int last=FindLastNum(arr,9,3);
int count=last-first+1;
cout<<count;
}
二.哈希表查找
哈希表最主要的优点是我们利用它能够在O(1)时间内查找某一元素,是效率最高的查找方式;但其缺点是需要额外的空间来实现哈希表。
题目第一个只出现一次的字符
在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符。
思路
处理字符串中重复或者次数出现等问题,最常用的就是哈希表,用字符串中的字符作为key,字符出现次数作为value,假定只有ASCII码范围内的字符,则可以开辟一个256大小的int数组,将每个字符(key)映射到该数组的对应位置上,计算每次出现的次数即可,遍历一次字符串,计算每个字符出现的次数,保存在int数组的对应位置上,第二次遍历字符串,若第一次出现某个字符对对应到的哈希表的对应位置处的元素为1,则该字符便是第一个只出现一次的字符,如果我们是遍历哈希表(int数组),则找到的哈希表中的第一个元素为1的位置对应的字符为字符串中第一个最小的只出现一次的字符。时间复杂度为O(n),需要额外的256个int空间来辅助,可以看做空间复杂度为O(1)。
代码
#include<iostream>
using namespace std;
char FirstNotRepeatingChar(char*str)
{
const int N=256;//char 8bit,256种
int hashtable[N];
for(int i=0;i<256;++i)
hashtable[i]=0;
char*phashkey=str;
while(*(phashkey)!='\0')
hashtable[*(phashkey++)]++;
phashkey=str;
while(*phashkey!='\0')
{
if(hashtable[*phashkey]==1)
return *phashkey;
phashkey++;
}
}
int main()
{
char str[]="abbaccdffj";
char res=FirstNotRepeatingChar(str);
cout<<res;
}