静态查找方法汇总

165 阅读2分钟

一般的静态查找方法

import java.lang.reflect.Array;
import java.util.Arrays;

public class StaticSearch {
    /***********************************************************************************************/
    //普通的顺序查找
    static int SequenceSearch1(int[] arr, int k)
    {
        for (int i = 0; i < arr.length; i++)
        {
            if (arr[i] == k)
                return i+1;
        }
        return -1;
    }
    /***********************************************************************************************/




    /***********************************************************************************************/
    //设置监视哨的顺序查找
    //不用每一步都检测整个表是否检查完毕,即每步都要检测i<arr.length的条件是否满足。
    static int SequenceSearch2(int[] arr, int k)
    {
        int i;
        //在低位设置一个监视哨
        int temp[] = new int[arr.length+1];
        System.arraycopy(arr, 0, temp, 0, 10); //用法 :从arr数组的0位置,将整个数组复制到temp数组的从0开始的位置。参与赋值的数组长度为10
        temp[10]=k;

        for (i = 0; temp[i] != k; i++);
        return i+1 ;
    }
    /***********************************************************************************************/



    //非递归的折半查找
    static int BinarySearch1(int[] arr, int k)
    {
        int low=0;
        int high=arr.length-1;
        while(low<=high)
        {
            int mid = (low+high)/2;
            if(k==arr[mid])
                return mid+1;
            else if(k<arr[mid])
                high=mid-1;
            else
                low=mid+1;
        }
        return -1;
    }
    /***********************************************************************************************/



    /***********************************************************************************************/
    //递归的折半查找
    static int BinarySearch2(int[] arr, int k,int low, int high)
    {
        while(low<=high) {
            int mid = (low+high)/2;
            if(k==arr[mid])
                return mid+1;
            else if(k<arr[mid])
                return BinarySearch2(arr,k,low,mid-1);
            else
                return BinarySearch2(arr,k,mid+1,high);
        }
        return -1;
    }
    /***********************************************************************************************/




    /***********************************************************************************************/
    //插值查找(折半查找的改进)
    /***
     在介绍插值查找之前,首先考虑一个新问题,为什么上述算法一定要是折半,而不是折四分之一或者折更多呢?
       打个比方,比如要在取值范围1 ~ 1000 之间 1000个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找,而不必从中间的500开始。即自适应确定查找位置
       经过以上分析,折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的)。二分查找中查找点计算如下:
       mid=(low+high)/2, 即mid=low+1/2*(high-low);
       通过类比,我们可以将查找的点改进为如下:
       mid=low+(key-a[low])/(a[high]-a[low])*(high-low),
       也就是将上述的比例参数1/2改进为自适应的,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。
       基本思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,插值查找也属于有序查找。
       注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
       复杂度分析:查找成功或者失败的时间复杂度均为O(log2(log2n))。
     */
    static int InsertionSearch(int[] arr, int k)
    {
        int low=0;
        int high=arr.length-1;
        while(low<=high)
        {
            int mid = low+(k-arr[low])/(arr[high]-arr[low])*(high-low);
            if(k==arr[mid])
                return mid+1;
            else if(k<arr[mid])
                high=mid-1;
            else
                low=mid+1;
        }
        return -1;
    }
    /***********************************************************************************************/



    /***********************************************************************************************/
    //构造一个斐波那契数组
    final static int max_size=100;
    static void Fibonacci(int[] F) {
        F[0]=1;
        F[1]=2;
        for(int i=2;i<max_size;i++) {
            F[i]=F[i-1]+F[i-2];
        }
    }
    static int FibonacciSearch(int[] arr, int k)
    {
        int low=0;
        int high=arr.length-1;
        int F[]=new int[max_size];
        Fibonacci(F);
        int p=0;
        while(arr.length>F[p]-1)   //计算arr.length位于斐波那契数列的位置
            ++p;
        int temp[]=new int[F[p]-1];  //将数组a扩展到F[p]-1的长度
        for(int i=0;i<arr.length;i++)
            temp[i]=arr[i];
        for(int i=arr.length;i<F[p]-1;i++)
            temp[i]=arr[arr.length-1];
        //System.out.println(F[p-1]);
        //System.out.println(Arrays.toString(temp));
        while(low<=high)
        {
            int mid = low+F[p-1]-1;
            //System.out.println(mid);
            if(k<temp[mid]) {
                high=mid-1;
                p-=1;
            }
            else if(k>temp[mid]) {
                low=mid+1;
                p-=2;
            }
            else {
                if(mid<arr.length)
                    return mid+1;        //若相等则说明mid即为查找到的位置
                else
                    return arr.length;    //若mid>=arr.length则说明是扩展的数值,返回arr.length-1
            }
        }
        return -1;
    }
    /***********************************************************************************************/


    public static void main(String[] arg)
    {
        int array[] = { 3, 6, 4, 8, 1, 0, 34, 7, 22, -2 };

        //数组被以下函数使用后不改变,仍然是无序的
        System.out.println("排序前:"+Arrays.toString(array));
        System.out.println(SequenceSearch1(array, 34) != -1 ? "普通顺序查找结果:第" + SequenceSearch1(array, 34) + "个数" : "普通顺序查找结果:不存在");
        System.out.println(SequenceSearch2(array, 34) <=array.length ? "哨兵顺序查找结果:第" + SequenceSearch2(array, 34) + "个数" : "哨兵顺序查找结果:不存在");


        //此后的数组在根本上被改变了,以下函数所用到的数组都是有序的
        Arrays.sort(array);
        System.out.println("排序后:"+Arrays.toString(array));
        System.out.println(BinarySearch1(array,34) != -1 ? "折半非递归查找结果:转化为有序数组后在第" + BinarySearch1(array,34) + "个数" : "折半非递归查找结果:不存在");
        System.out.println(BinarySearch2(array,34,0,array.length-1) != -1 ? "折半递归查找结果:转化为有序数组后在第" + BinarySearch2(array,34,0,array.length-1) + "个数" : "折半递归查找结果:不存在");
        System.out.println(InsertionSearch(array, 34) != -1 ? "插入查找结果:转化为有序数组后在第" + InsertionSearch(array, 34) + "个数" : "插入查找结果:不存在");
        System.out.println(FibonacciSearch(array, 34) != -1 ? "斐波那契查找结果:转化为有序数组后在第" + FibonacciSearch(array, 34) + "个数" : "斐波那契查找结果:不存在");





    }

}



分块查找

1、Java实现

import java.util.Collections;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.Arrays;

class Index {
	// 定义块的结构
	int key;
	int start;

	public Index(int key, int start) {
		this.key = key;
		this.start = start;
	}
}

public class BlockSearch {
	static ArrayList<Index> indexs = new ArrayList<>();

	static int search(int key, int a[]) {
		int i=0, startValue;
		// 确定在哪个块中,遍历每个块,确定key在哪个块中
		while (i < 3 && key > indexs.get(i).key) {
			i++;
		}
		//大于所分的块数,说明不存在key值
		if (i >= 3) {
			return -1;
		}

		startValue = indexs.get(i).start; // startValue等于块范围的起始值
		
		while (startValue <= startValue + 5 && a[startValue] != key) {
			startValue++;
		}
		//在块内查找无果,说明不存在key值
		if (startValue > startValue + 5) {
			return -1;
		}
		return startValue;
	}

	public static void test() {

		// Index indexs[]=new Index[3];
		int i, j = -1, k, key, start;
		int a[] = { 33, 42, 44, 38, 24, 48, 22, 12, 13, 8, 9, 20, 60, 58, 74, 49, 86, 53 };

		// 确认模块的起始值和最大值.分成3块(可自定义)
		for (i = 0; i < 3; i++) {
			start = j + 1;    //起始值
			key = a[start];   //关键值(最大值)
			j += 6;
			for (k = start; k <= j; k++) {
				if (a[k] >= key) {
					key = a[k];
				}
			}
			indexs.add(new Index(key,start));
		}
		//以上的for循环确认模块的起始值和最大值,等价于以下语句
		/***
		 * indexs.add(new Index( 48,0)); 
		 * indexs.add(new Index( 22,6));
		 * indexs.add(new Index( 86,12));
		 ***/

		System.out.println("排序前:");
		for (Index index : indexs) {
			System.out.println("关键值key:" + index.key + " 起始地址start:" + index.start);
		}
		
		//对结构体(类)按key值排序
		Collections.sort(indexs, new Comparator<Index>() {
			public int compare(Index o1, Index o2) {
				return o1.key - o2.key; 
			}
		});
		System.out.println("排序后:");
		for (Index index : indexs) {
			System.out.println("关键值key:" + index.key + " 起始地址start:" + index.start);
		}
		
		// System.out.println("请输入您想要查找的数:");
		for (i = 0; i < 18; i++) {
			k = search(a[i], a);
			if (k >= 0) {
				System.out.println("查找成功!您要找的数" + a[i] + "在数组中的位置是:" + (k + 1));
			} else {
				System.out.println("查找失败!您要找的数不在数组中。");
			}
		}
	}
}


2、C实现

#include <stdio.h>
#include <stdlib.h>

struct index
{
  //定义块的结构
  int key;
  int start;

} newIndex[3];  //定义结构体数组
int search(int key, int a[]);

int cmp(const void *a,const void* b)
{
  return (*(struct index*)a).key>(*(struct index*)b).key?1:-1;
}

int main()
{
  int i, j=-1, k, key;
  int a[] = {33,42,44,38,24,48, 22,12,13,8,9,20, 60,58,74,49,86,53};
  //确认模块的起始值和最大值
  for (i=0; i<3; i++)
  {
    newIndex[i].start = j+1; //确定每个块范围的起始值
    j += 6;
    for (int k=newIndex[i].start; k<=j; k++)
    {
      if (newIndex[i].key<a[k])
      {
        newIndex[i].key = a[k];
      }
    }
  }
  //对结构体按照 key 值进行排序
  qsort(newIndex,3, sizeof(newIndex[0]), cmp);
  //输入要查询的数,并调用函数进行查找
  printf("请输入您想要查找的数:\n");
  scanf("%d", &key);
  k = search(key, a);
  //输出查找的结果
  if (k>0)
  {
    printf("查找成功!您要找的数在数组中的位置是:%d\n",k+1);
  }
  else
  {
    printf("查找失败!您要找的数不在数组中。\n");
  }

  return 0;
}

int search(int key, int a[])
{
  int i, startValue;
  i = 0;
  while (i<3 && key>newIndex[i].key)
  {
    // 确定在哪个块中,遍历每个块,确定key在哪个块中
    i++;
  }
  if (i>=3)
  {
    //大于分得的块数,则返回0
    return -1;
  }
  startValue = newIndex[i].start; //startValue等于块范围的起始值
  while (startValue <= startValue+5 && a[startValue]!=key)
  {
    startValue++;
  }
  if (startValue>startValue+5)
  {
    //如果大于块范围的结束值,则说明没有要查找的数
    return -1;
  }

  return startValue;
}