Algorithm4——有序向量查找

来源:互联网 发布:网络高清硬盘播放机 编辑:程序博客网 时间:2024/05/16 09:16

本系列算法实现都是在学习数据结构(C++语言版),清华大学邓俊辉教授编,听邓老师edX网课过程中自己实现的例子。

问题: 对有序向量中某一个元素的查找,并返回其位置?
解决: 用三种算法实现

  • 二分查找
  • Fibonacci查找
  • 插值查找

1 二分查找

二分查找的原理很简单,分治的思想,此处用迭代和递归两种方法

算法复杂度为O(1.5logN)

1.1 迭代版本

//! array is already sorted by <//! [low, high)//! o(1.5*logn) 查找过程并不平衡,因为向左、向右两个分支所需要的比较次数不相等template <typename T, typename V>int search_binary(T array, int low, int high , V const& elem){    int mid;    while (low < high)    {        mid = (low + high) >> 1;//divided分治        if (array[mid] > elem) //compare 1            high = mid;        else if (array[mid] < elem)//compare 2            low = mid + 1;        else            return mid;//compare 3    }    return -1;}

1.2 递归版本

//! array is already sorted by <//! [low, high)//! o(1.5*logn) 查找过程并不平衡,因为向左、向右两个分支所需要的比较次数不相等template <typename T, typename V>int search_binary_recursion(T array, int low, int high , V const& elem){    int mid;    while (low < high)    {        mid = (low + high) >> 1;        if (array[mid] > elem) //compare 1            return search_binary_recursion(array, low, mid, elem);        else if (array[mid] < elem) //compare 2            return search_binary_recursion(array, mid + 1, high, elem);        else            return mid;//compare 3    }    return -1;}

2 Fibonacci查找

Fibonacci查找相比二分查找在算法复杂度的常系数上进行了优化。

Fibonacci查找算法复杂度推导过程如下图,字写得乱,海涵……

具体推导请看第二章 向量(下(d3)有序向量:Fibonacci查找 02D3-4: 最优性

推导过程

算法复杂度为O(1.44logN)

二者区别是选取轴点mid的方式不同。
二分查找

mid = (low + high) >> 1;

Fibonacci查找

mid = low + fib.get_cur() - 1; //确定形如fib(k) - 1的轴点

代码实现如下,其中用到的Fibonacci类实现在文章Algorithm2——斐波那契数列的最下面。

//! array is already sorted by <//! [low, high)//! o(1.44*logn)//! search_fibonacci()与search_binary()区别二者选取轴点mid的方式不同template <typename T, typename V>int search_fibonacci(T array, int low, int high , V const& elem){    int mid;    Fibonacci fib(high - low);    while (low < high)    {        while ( high - low < fib.get_cur())            fib.get_pre(); //通过向前顺序查找        mid = low + fib.get_cur() - 1; //确定形如fib(k) - 1的轴点        if (array[mid] > elem) //compare 1            high = mid;        else if (array[mid] < elem)//compare 2            low = mid + 1;        else            return mid;//compare 3    }    return -1;}

3 插值查找

插值查找的核心是根据元素增长分布的特性使得能够更快的找到mid位置,此算法的复杂度为O(loglogN)

公式如下

midlowhigh1low=elemA[low]A[high1]A[low]

其中在数组A[low, high),查找的元素值为elem,mid是插值后计算得到的中间值。
写成C++代码就是

mid = (1.0f * (elem - array[low]) / (array[high - 1] - array[low]) * (high - 1 - low) + low);

代码实现如下

//! array is already sorted by <//! [low, high)//! O(loglogN)template <typename T, typename V>int search_interpolation(T array, int low, int high , V const& elem){    int mid;    while (low < high)    {        std::cout << "+ ";        mid = (1.0f * (elem - array[low]) / (array[high - 1] - array[low]) * (high - 1 - low) + low);        if (array[mid] > elem) //compare 1            high = mid;        else if (array[mid] < elem)//compare 2            low = mid + 1;        else            return mid;//compare 3    }    return -1;}

测试代码为

   cout << "----- search array -----" << endl;   int a[6] = {0, 1, 2, 3, 4, 5};   int len = sizeof(a) / sizeof(a[0]);   for (int i = 0; i < len; ++i)   {       cout << "interpolation    " ;       cout << search_interpolation(a, 0, len, a[i]) << endl;       cout << "binary_recursion ";       cout << search_binary_recursion(a, 0, len, a[i]) << endl;       cout << endl << endl;   }

运行结果如下。
‘+’符号的个数表示查找成功时已经在while循环运行的次数,可见插值查找确实比二分查找在元素均匀分布时更有优势。

----- search array -----interpolation    + 0binary_recursion + + + 0interpolation    + 1binary_recursion + + 1interpolation    + 2binary_recursion + + + 2interpolation    + 3binary_recursion + 3interpolation    + 4binary_recursion + + + 4interpolation    + 5binary_recursion + + 5
原创粉丝点击