计算机基础——7种基础查找算法

本文介绍了计算机基础中的七种查找算法,包括顺序查找、二分查找、插值查找、斐波那契查找、分块查找、哈希查找和树表查找。其中,顺序查找适用于无序数据,时间复杂度为O(n);二分查找要求数据有序,时间复杂度为O(logn);插值查找在分布均匀时更有效;斐波那契查找使用斐波那契数列作为查找点;分块查找通过索引加速查找;哈希查找利用哈希表快速定位,但需处理哈希冲突;树表查找则基于各种树结构进行查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

查找

查找算法的分类:

1.静态查找和动态查找:动态和静态都是相对于表而言的。动态表中有删除和插入操作。
2.无序查找和有序查找:被查找的数列是否有序。

1.顺序查找

基本思想:顺序查找就是按照顺序,从数据结构的一端,顺序扫描,直到查找成功。
平均查找长度ASL:(n+1)/2
时间复杂度:O(n)
代码:

/**
     * 顺序查找
     * 思想:从前到后遍历,直到找到要找的元素,返回元素下标,
     * 优点:二叉查找对于等概率的查找的性能是最优的。
     * 时间复杂度:O(n)
     * @param list
     * @param x
     */
    public static void Sort_Search(int list[],int x){
        for(int i=0;i<list.length;i++){
            if(list[i] == x){
                System.out.println("元素处在第"+i+"位");
            }
        }
    }

2.二分查找

前提:元素必须是有序的。而且是顺序表存储结构
基本思想:将目标元素与位于(1+n)/2位置的元素相比较,如果小于就在小于(1+n)/2的子表中继续上述操作,如果大于,就在大于中间值的子表中查找。
时间复杂度:O(logn)
代码:

/**
     * 二分查找
     * 前提:队列必须是有序的
     * 思想:确定中间位置,然后与待查询值相比,
     *      如果小于,则与左边数组中间值比较,
     *      如果等于,则返回这个值,如果大于,则与右边数组中间值相比较
     *时间复杂度:O(logn)
     * @param list
     * @param x
     */
    public static void Binary_Search(int list[],int x){

        Quick_Sort(list,0,list.length-1);
        show_list(list);
        int ad = Recursion(list,x,0,list.length-1);

        System.out.println("元素处在第"+ad+"位");
    }
    /**
     * 二分查找的递归
     */
    public static int Recursion(int list[],int x,int left,int right){

        int mid = (left+right)/2;
        if(list[mid] == x )return mid;
        else if(list[mid] > x) return Recursion(list,x,left,mid-1);
        else return Recursion(list,x,mid+1,right);
    }

3.插值查找

基本思想:差值查找法是基于二分法进行改进的算法,与二分法不同的是,差值法选择的不是中间值。而是根据所要查找的数进行自适应选择。mid=low+(key-a[low])/(a[high]-a[low])*(high-low)。
特点:当有序表的元素分布比较均匀时,插值查找比折半查找要好得多。
如果分布不均匀,则会花费更多时间。
时间复杂度:O(log(logn))

/**
     * 插值查找
     * 思想:基于二分查找
     *      定义mid为mid = (high-low)*(key-arr[low])/(arr[high]-arr[low])
     *       中间值的定义,与数组的极大值和极小值有关。
     * 时间复杂度:平均O(loglogn),最坏O(logn)
     * 优点:对于表长较大,而关键字分布又比较**均匀**的查找表来说,插值查找的平均性能比折半查找要好。
     */
    public static void Interpolation_Search(int list[],int x){
        Quick_Sort(list,0,list.length-1);
        show_list(list);
        int ad = Recursion(list,x,0,list.length-1);

        System.out.println("元素处在第"+ad+"位");
    }
    /**
     * 插值查找的递归
     */
    public static int Interpolation_Recursion(int list[],int x,int left,int right){

        int mid = 0;
        mid = (right-left)*(x-list[left])/(list[right]-list[left]);
        if(list[mid] == x )return mid;
        else if(list[mid] > x) return Interpolation_Recursion(list,x,left,mid-1);
        else return Interpolation_Recursion(list,x,mid+1,right);
    }

4.斐波拉契查找(黄金分割查找){1,1,2,3,5,8,13,21,34}

基本思想:斐波拉契查找是相对于二分查找和插值查找来区分的。具体思想差不多,主要区别在于插值的选取。
假设有待查找数组array[n]和斐波那契数组F[k],并且n满足n>=F[k]-1&&n < F[k+1]-1,则它的第一个拆分点middle=F[k]-1。意思就是所,取下标为斐波拉契数的数值。
特点:此查找的key只涉及加法运算,相比于二分查找用到了除法,可能斐波拉契查找的速度要快一些。
时间复杂度:O(logn)

二分查找,插值查找,斐波拉契查找,三种查找的区别
当数据分布较为均匀时,插值查找的时间复杂度会大大降低。但每次运算获得插值时,运算比较比较耗费时间。
二分查找与斐波拉契查找哪个更优,暂时没有找到答案。但当二分查找中的除法使用>>1来代替,可能效率将更高一点。
参考:斐波拉契和二分哪个更快?

5.分块查找(索引查找)

要求:分块查找的要求,就是要,数据基本有序或者,分块有序。
也就是要保证,每一块中最小值要大于前一块的最大值。

分块查找的思想就是,将数据分成多个块,先判断数据在哪个块范围,再在块内找。

分块查找的实现建立一个索引结构,保存每个块中的首地址和最小数据。如果每块不是有序的,需要先将块排序。然后再将value与每块的最小值比较,判断在那一块,然后在块中查找对应的value。
代码实现引用用c实现块查找

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

//定义块结构
struct index
{
	int key;   //块的key值
	int start; //块的起始值
}newIndex[3];//此处同时创建三个块索引

//定义查找函数
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;

}


//定义比较函数:用于给块排序
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; //start指向首地址
		j += 6;
		for (int k = newIndex[i].start; k <= j; k++) {
			if (newIndex[i].key < a[k]) {
				newIndex[i].key = a[k];   //key保存最小值
			}
		}
	}

	//对结构体按照key值进行排序
	//qsort函数:快速排序函数
	//函数有4个参数值:第一个为待排序数组的首地址,
	//				   第二个为数组的长度,
	//                 第三个为数组元素所占的字节,
	//                 第四个为自定义比较函数。
	qsort(newIndex, 3, sizeof(newIndex[0]), cmp);
	//输出要查询的数,并调用函数进行查找
	printf("请输入你想要查找的数:\n");
	scanf_s("%d", &key);
	k = search(key, a);
	//输出查找的结果
	if (k > 0) {
		printf("查找成功!位置是:%d\n", k + 1);
	}
	else {
		printf("查找失败!您要找的数不在数组中。\n");
	}

	system("pause");

	return 0;

}

6.哈希查找

**思想**:hash查找是一种以空间换时间的算法,比如原来一个长度为100的数组,对其查找,只需要遍历匹配即可,从空间复杂度上来说,加入数组存储的是byte型数据,name该数组占用100byte空间。现在我们采用Hash算法,用来约束键与存储位置的关系,那么就需要固定一个长度的hash表,如果仍为100byte,那么此时我们需要的总空间为200byte,而且用于记录规则的表的大小会根据规则的变化改变,大小可能不确定。
**流程**:1.用哈希函数,构造哈希表。
				 2.根据选择冲突处理方法解决地址冲突,常见的解决冲突的方法:拉链法和线性探测法。

代码实现:

import java.io.IOException;
import java.util.Scanner;

public class HashSearch {
    // 初始化哈希表
    static int hashLength = 7;
    static int[] hashTable = new int[hashLength];

    // 原始数据
    static int[] list = new int[]{13, 29, 27, 28, 26, 30, 38};

    public static void main(String[] args) throws IOException {
        System.out.println("*******哈希查找*******");

        // 创建哈希表
        for (int i = 0; i < list.length; i++) {
            insert(hashTable, list[i]);
        }
        System.out.println("展示哈希表中的数据:" + display(hashTable));

        while (true) {
            // 哈希表查找
            System.out.print("请输入要查找的数据:");
            int data = new Scanner(System.in).nextInt();
            int result = search(hashTable, data);
            if (result == -1) {
                System.out.println("对不起,没有找到!");
            } else {
                System.out.println("数据的位置是:" + result);
            }
        }
    }

    /**
     * 方法:哈希表插入
     */
    public static void insert(int[] hashTable, int data) {
        // 哈希函数,除留余数法
        int hashAddress = hash(hashTable, data);

        // 如果不为0,则地址已被占用,说明发生冲突
        while (hashTable[hashAddress] != 0) {
            // 利用 开放定址法 解决冲突
            hashAddress = (++hashAddress) % hashTable.length;
        }

        // 将待插入值存入字典中
        hashTable[hashAddress] = data;
    }

    /**
     * 方法:哈希表查找
     */
    public static int search(int[] hashTable, int data) {
        // 哈希函数,除留余数法
        int hashAddress = hash(hashTable, data);

        while (hashTable[hashAddress] != data) {
            // 利用 开放定址法 解决冲突
            hashAddress = (++hashAddress) % hashTable.length;
            // 查找到开放单元 或者 循环回到原点,表示查找失败
            if (hashTable[hashAddress] == 0 || hashAddress == hash(hashTable, data)) {
                return -1;
            }
        }
        // 查找成功,返回下标
        return hashAddress;
    }
    /**
     * 方法:构建哈希函数(除留余数法)
     *
     * @param hashTable
     * @param data
     * @return
     */
    public static int hash(int[] hashTable, int data) {
        return data % hashTable.length;
    }

    /**
     * 方法:展示哈希表
     */
    public static String display(int[] hashTable) {
        StringBuffer stringBuffer = new StringBuffer();
        for (int i : hashTable) {
            stringBuffer = stringBuffer.append(i + " ");
        }
        return String.valueOf(stringBuffer);
    }
}
哈希冲突解决方法

待总结!!!

7.树表查找

基于各种树的数表查找

待总结!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值