算法 - 查找 - 插值查找 (Interpolation Search)

算法 - 查找 - 插值查找 (Interpolation Search)

返回分类:全部文章 >> 基础知识

返回上级:算法 - 查找与排序 (Searching and Sorting)

本文将用C++实现通用模板插值查找算法,复制代码直接可使用。

在查看本文之前,需要一些程序语言的基础。

还需要熟悉 算法 - 查找 - 二分查找 (Binary Search) 。


文章目录

  • 算法 - 查找 - 插值查找 (Interpolation Search)
    • 1 插值查找简述 (Introduction)
    • 2 整型查找 (Interger Search)
    • 3 模板查找 (Template Search)
    • 4 修改整型查找 (Modify Interger Search)
    • 5 自定义类型的调用 (Custom Type)


1 插值查找简述 (Introduction)

插值查找,它是二分查找的变种,它只适用于有序递增表。其时间复杂度 O(log2n) 。

插值查找改变了二分查找中原有的中值 mid 的求解方式,其 mid 不再代表中值,而是使用了一个插值公式:

m i d = l e f t + ( x − V a l [ l e f t ] ) × ( r i g h t − l e f t ) V a l [ r i g h t ] − V a l [ l e f t ] mid = left + \frac{(x - Val[left]) \times (right - left)}{Val[right] - Val[left]} mid=left+Val[right]Val[left](xVal[left])×(rightleft)

假设表中有 n 个元素,查找过程为取区间中间元素的下标 mid ,对 mid 的关键字与给定值的关键字比较:

  • (1)如果与给定关键字相同,则查找成功,返回在表中的位置;

  • (2)如果给定关键字大,则更新左区间起始位置等于 mid + 1 ,即向右查找;

  • (3)如果给定关键字小,查找的值不在范围,直接返回;

  • (4)重复过程,直到找到关键字(成功)或区间为空集(失败)。

通常情况下:

  • 返回值,代表下标;

  • 返回-1,代表没有找到关键字;

之后的程序,我们以数组列表形式描述。

注意:代码全部使用std::vector作为数组列表,如果你用指针数组MyType*,还需传入数组大小size


2 整型查找 (Interger Search)

一般举例中,查找最基本的元素本身就是整型。

// Author: https://blog.csdn.net/DarkRabbit
// Interpolation Search
// 模板有序递增表 - 插值查找 O(log2n)
// params:
//      list:       查找的线性递增表
//      element:    查找的元素
// return:
//      int:        找到的下标,-1为表中没有
int InterpolationSearch(const std::vector<int>& list,
                        const int& element)
{
    if (list.empty())
    {
        return -1;
    }

    int size = list.size();
    int left = 0;
    int right = size - 1;

    int minIndex = left;
    int maxIndex = right;
    int maxKey = list[maxIndex];

    int leftKey;
    int mid;
    while (left < maxIndex)
    {
        if (list[left] == maxKey) // 有重复值
        {
            return -1;
        }

        mid = left + (maxIndex - left) * (element - list[left]) / (list[maxIndex] - list[left]);
        if (mid < minIndex || mid > maxIndex) // 检测值小于最小值或大于最大值
        {
            return -1;
        }

        if (element > list[mid]) // x > mid ,向右查找
        {
            left = mid + 1;
        }
        else if (element < list[mid]) // x < mid 查找的值不在范围,返回-1
        {
            break;
        }
        else
        {
            return mid;
        }
    }

    return -1;
}

3 模板查找 (Template Search)

在实际应用中,通常情况下,列表存储的都是一些数据(结构体或类),它们都包含唯一标识(即关键字Key)。

我们一般不会将它们的关键字重新建立一个列表,再去查找。

这在C++中通常用模板 (template) 来解决,其它语言多数用泛型 (Genericity) 来解决。

我们的要求仅仅是用结构中的关键字进行比较,即我们只关心关键字而不关心这个数据的类型。这样使用自定义类型也不怕了。

由于插值查找会进行数学运算,所以可以使用一个函数指针传入获取关键字的函数,在函数中自定义获取关键字。

我们规定此函数指针的结果:关键字。

我们可以限定给定区间,不一定扫描整个表,这样在其它算法中可以有效调用它。

我们接下来改造成模板函数:

// Author: https://blog.csdn.net/DarkRabbit
// Interpolation Search

// 模板有序递增表 - 插值查找 O(log2n)
// params:
//      list:       查找的线性递增表
//      element:    查找的元素
//      left:       开始查找下标
//      right:      结束查找下标
//      pKey:       获取比较的键值
// return:
//      int:        找到的下标,-1为表中没有
template<typename T>
int InterpolationSearch(const std::vector<T>& list,
                        const T& element,
                        int left,
                        int right,
                        int(*pKey)(const T&))
{
    if (pKey == nullptr || list.empty())
    {
        return -1;
    }

    if (left > right) // 交换
    {
        int tmp = left;
        left = right;
        right = tmp;
    }

    if (left < 0)
    {
        left = 0;
    }

    if (right >= list.size())
    {
        right = list.size() - 1;
    }

    int size = list.size();
    int key = pKey(element);
    int minIndex = left;
    int maxIndex = right;
    int maxKey = pKey(list[right]);

    int leftKey;
    int mid;
    int midKey;
    while (left < maxIndex)
    {
        leftKey = pKey(list[left]);
        if (leftKey == maxKey) // 有重复值
        {
            return -1;
        }

        mid = left + (maxIndex - left) * (key - leftKey) / (maxKey - leftKey);
        if (mid < minIndex || mid > maxIndex) // 检测值小于最小值或大于最大值
        {
            return -1;
        }

        midKey = pKey(list[mid]);
        if (key > midKey) // x > mid ,向右查找
        {
            left = mid + 1;
        }
        else if (key < midKey) // x < mid 查找的值不在范围,返回-1
        {
            break;
        }
        else
        {
            return mid;
        }
    }

    return -1;
}

// 模板有序递增表 - 插值查找
// params:
//      list:       查找的线性递增表
//      element:    查找的元素
//      pKey:       获取比较的键值
// return:
//      int:        找到的下标,-1为表中没有
template<typename T>
int InterpolationSearch(const std::vector<T>& list,
                        const T& element,
                        int(*pKey)(const T&))
{
    return InterpolationSearch(list, element, 0, list.size(), pKey);
}

4 修改整型查找 (Modify Interger Search)

有了模板函数后,我们之前的整型函数可以进行修改,直接调用模板函数即可。

我们在这里直接传入 Lambda 表达式:

// Author: https://blog.csdn.net/DarkRabbit
// Interpolation Search

// 整型有序递增表 - 插值查找
// params:
//      list:       查找的线性递增表
//      element:    查找的元素
//      left:       开始查找下标
//      right:      结束查找下标
// return:
//      int:        找到的下标,-1为表中没有
int InterpolationSearch(const std::vector<int>& list,
                        const int& element,
                        int left,
                        int right)
{
    return InterpolationSearch<int>(list, element, left, right,
                                    [](const int& x)->int
    {
        return x;
    });
}

// 整型有序递增表 - 插值查找
// params:
//      list:       查找的线性递增表
//      element:    查找的元素
// return:
//      int:        找到的下标,-1为表中没有
int InterpolationSearch(const std::vector<int>& list,
                        const int& element)
{
    return InterpolationSearch<int>(list, element, 0, list.size(),
                                    [](const int& x)->int
    {
        return x;
    });
}

5 自定义类型的调用 (Custom Type)

类似的,自定义类型调用:

// Author: https://blog.csdn.net/DarkRabbit
// Interpolation Search

#include 
#include 
#include 
using namespace std;

struct MyElement
{
    int key;
    string data;
};

int MyInterpolationKey(const MyElement& x)
{
    return x.key;
}

int main()
{
    vector<MyElement> list; // 列表
    MyElement tofind; // 需要查找的元素

    // TODO 省略初始化列表和元素的过程

    int index = InterpolationSearch<MyElement>(list, tofind, 
                [](const MyElement& x)->int
                {
                    return x.key;
                });

    // 以上调用方法等同于
    // int index = InterpolationSearch(list, tofind, MyInterpolationKey);

    if (index != -1)
    {
        // do something
        cout << "找到了下标:" << index << endl;
    }

    return 0;
}

你可能感兴趣的:(基础知识,插值,查找,算法,C++)