一、静态查找
1.无序查找:
数组存储的元素是无序的。
顺序查找算法:
从所有元素按下标比较一遍,通过下标返回查找的结果。
publicstatic int Sequential_Search(int[] a,int k){
int i;
for(i=0;i<a.length-1;i++){
if(a[i]==k)
returni;
}
return-1;
}
public staticint Sequential_search2(int a[],int k){
int i=a.length;
a[0]=k;;
while(a[i]!=k){
i–;
}
return i;
}
这两种顺序查找算法看似一样,实际上是不同的。第一种在for循环中需要不断去用i和数组的长度比较。而第二种省去了这一步,但是额外利用a[0]作为哨兵,数组的存储是从1开始的。在速度上,第二种比第一种要快一些。时间复杂度都是o(n)。
2.有序查找
数组存储的元素是有序的,下面用到的查找技术都要依赖于这个前提。
2.1折半查找:
因为元素有序,我们可以从中间开始比较,若k<a[mid],则范围锁定前半区域,否则从后半区域查找。
//递归查找算法。
public class Binary {
public static voidmain(String args[]){
int[]a={1,2,3,4,5,6,7,8,9};
System.out.println(binary_search(a,0, a.length-1, 9));
}
public static intbinary_search(int[] a,int h,int t,int k){
if(h>t){
return -1;
}
int mid=(h+t)/2;
if(k<a[mid]){
return binary_search(a,h, mid-1, k);
}else if(k>a[mid]){
return binary_search(a,mid+1, t, k);
}else{
returnmid;
}
}
}
折半查找算法使用分治的思想,时间复杂度为o(logn),优于顺序查找算法。
2.2差值查找:
为什么非要使用二分查找,我们就不能三分,四分吗?试想一下,如果有1到100,有100个数,我们查找2的话,也要比较很多次,因此,为了把数值大小的影响考虑进来,我们使用差值查找。核心代码只需要修改mid的值,此时mid=h+((k-a[h])/(a[t]-a[h]))*(t-h),还是说刚才的找2,mid=0+(2-1)/(100-1) *(99-0)=1而a[1]=2就变得非常的快。
//递归查找算法
public static int InsertionSearch(int a[], int value, int h, intt)
{
if(h>t){
return -1;
}
int mid = h+(value-a[h])/(a[t]-a[h])*(t-h);
if(a[mid]==value){
return mid;
}else if(a[mid]>value){
return InsertionSearch(a, value, h, mid-1);
}else{
return InsertionSearch(a, value, mid+1, t);
}
}
时间复杂度也是logn,显然速度上要优于折半,但是如果数值不均匀的话就不一定好用了,例如{1,100,101,102…199}。
2.3斐波那契查找:
顾名思义,这里使用到的位置范围既不是使用二分,也不是使用查分,而是使用到斐波那契数列的元素。斐波那契数列的前后两个元素的比值,随着位置的变大,不断接近于0.614:1,不错,这里用到的是黄金分割,一个完美的划分点。
public class fibonacci {
static int[] F=new int[100];
public static voidmain(String args[]){
F[0]=0;
F[1]=1;
for(inti=2;i<100;i++){
F[i]=F[i-1]+F[i-2];
}
int[]a={1,3,5,7,9,11,13,15,17};
ArrayList<Integer>list=new ArrayList<Integer>();
for(inti=0;i<a.length;i++){
list.add(a[i]);
}
System.out.println(Fibonacii_Search(list,7, a.length-1));
}
public static intFibonacii_Search(ArrayList<Integer> a,int key,int n){
intlow,high,mid,i,k;
low=0;
high=n-1;
k=0;
while(n>F[k])
k++;
for(i=0;i<F[k];i++)//这个地方注意,a数组的长度是变化的,补全值的需要。
a.add(a.get(n));
while(low<=high){
mid=low+F[k-1]-1;
if(key<a.get(mid)){
high=mid-1;
k=k-1;
}else if(key>a.get(mid)){
low=mid+1;
k=k-2;
}else{
if(mid<=n)
returnmid;
else
returnn;
}
}
return 0;
}
}
这里为啥用k-1和k-2,用一个图就可以理解了。
线性索引查找:
在实际应用中,例如网站帖子和磁盘文件都是很长的,保证全部有序是很困难的,而且数据增长很快。因此比较建议的方法是使用索引。
1稠密索引:
如图所示,我们可以把关键码进行排序。当要查找某个关键码的数据时,就可以利用索引,先进性二分或者查分等方法,便可以降低时间复杂度。
2分块索引
块内无须,块间有序,索引表存放最大关键码,块长和块首指针。在即使均使用顺序查找,时间复杂度也小于o(n),是根号下n的复杂度。n条记录被分成m块,每块有t条记录。索引表依然可以利用二分和查分算法。
3、倒排索引
搜索引擎使用到的就是倒排索引算法。互联网拥有大量的网页,人们在使用搜索引擎的时候需要得到相关的推荐结果。对于关键字和网页的关系的组织是很重要的。
如图所示就是倒排索引的结构,当然实际中,文章编号一栏会有很多很多,因此涉及的算法和结构也很复杂,这里只是简单的表示。