Skip to content

Latest commit

 

History

History
282 lines (196 loc) · 6.85 KB

折半查找.md

File metadata and controls

282 lines (196 loc) · 6.85 KB

折半查找

折半查找

在数组中搜索元素的一般方法是使用一个for循环来遍历数组中的元素。例如,下面的代码将搜索数组中的元素x:

int findNum(int a[], int len,int x)
{
    int i;
    for(i = 0; i < len; i++)
        if( a[i] == x)
            return 1; // 表示找到
    return 0;
}

完整代码:

#include <iostream>
using namespace std;

int findNum(int a[], int len,int x)
{
    int i;
    for(i = 0; i < len; i++)
        if( a[i] == x)
            return 1; // 表示找到
    return 0;
}
int main()
{
    int array[] = {2,5,1,67,34,21,78};
    int len = sizeof(array) / sizeof(int);
    int x;

    cout << "input x:"; cin >> x;
    if(findNum(array,len,x)== 1)
         cout << x << " find! " << endl;
    else cout << x << " not find! " << endl;

    return 0;
    
}

这种方法的时间复杂度是O (n),因为在最坏的情况下,有必要检查数组中的所有元素。如果元素的顺序是任意的,这也是最好的方法,因为在数组中没有我们应该在什么地方搜索元素x的附加信息。

但是,如果对数组进行了排序,那么情况就不同了。在这种情况下,可以更快地执行搜索,因为数组中元素的顺序会指导搜索。下面的折半搜索算法在O(logn)时间内有效地搜索已排序数组中的一个元素。

方法一:

实现折半查找的通常方法类似于在字典中寻找一个单词。搜索将在数组中维护一个活动区域,该区域最初包含所有数组元素。然后,执行一些步骤,每个步骤都是区域大小的一半。在每一步中,搜索都会检查活动区域的中间元素。如果中间的元素是目标元素,则搜索将终止。否则,搜索递归地继续到区域的左或右半部分,这取决于中间元素的值。

int binarySearch(int a[], int len,int x)
{
    int i = 0, j = len -1;

    while(i <= j)
    {
        int k = ( i + j) / 2;  //中间位置的下标
        if(a[k] == x)
            { return 1;}
        if(a[k] > x)  j = k - 1;
        else  i = k + 1;   
    }

    return 0;
}

完整代码:

#include <iostream>
#include <algorithm>

using namespace std;

int binarySearch(int a[], int len,int x)
{
    int i = 0, j = len -1;

    while(i <= j)
    {
        int k = ( i + j) / 2;  //中间位置的下标
        if(a[k] == x)
            { return 1;}
        if(a[k] > x)  j = k - 1;
        else  i = k + 1;   
    }

    return 0;
}

int main()
{
    int array[] = {2,5,1,67,34,21,78};
    int len = sizeof(array) / sizeof(int);
    int x;

    cout << "input x:"; cin >> x;
    sort(array,array + len) ; //排序
    if(binarySearch(array,len,x)== 1)
         cout << x << " find! " << endl;
    else cout << x << " not find! " << endl;

    return 0;
    
}

说明:

在这个实现中,活动区域是一个i……j,初始区域是0…n−1。该算法在每一步中都将区域的大小减半,因此时间复杂度为O(logn)。

方法二:

实现折半查找的另一种方法是基于一种迭代数组元素的有效方法。其想法是,当我们接近目标元素时,进行跳跃并减缓速度。

搜索从左到右穿过数组,初始跳转长度为n/2。在每一步中,跳跃的长度将被减半:先是n/4,然后是n/8,n/16,等等,直到最后长度为1。跳转之后,要么找到目标元素,或者我们知道它没有出现在数组中。

函数定义如下:

int binarySearch(int a[], int len,int x)
{
    int k = 0;

    for(int b = len / 2; b >= 1; b /= 2)
    {
        while( k+b < len && a[k + b] <= x ) k += b;   //注意这里
    }

    if(a[k] == x)
        return 1;
    

    return 0;
}

完整代码如下:

#include <iostream>
#include <algorithm>

using namespace std;

int binarySearch(int a[], int len,int x)
{
    int k = 0;

    for(int b = len / 2; b >= 1; b /= 2)
    {
        while( k+b < len && a[k + b] <= x ) k += b;
    }

    if(a[k] == x)
        return 1;
    

    return 0;
}

int main()
{
    int array[] = {2,5,1,67,34,21,78};
    int len = sizeof(array) / sizeof(int);
    int x;

    cout << "input x:"; cin >> x;
    sort(array,array + len) ; //排序
    if(binarySearch(array,len,x)== 1)
         cout << x << " find! " << endl;
    else cout << x << " not find! " << endl;

    return 0;
    
}

在搜索过程中,变量b包含当前的跳转长度。该算法的时间复杂度为O(logn),因为当跳转循环中的代码对每个跳转长度最多执行两次。

STL中相关查找函数

C++标准库包含以下基于折半查找和在对数时间内工作的函数:

  • lower_bound:返回一个指向其值至少为x的第一个数组元素的指针。
  • upper_bound:返回一个指向其值大于x的第一个数组元素的指针。
  • equal_range: 返回以上两个指针。

这些函数假定该数组是已排序的。如果没有这样的元素,指针将指向最后一个数组元素之后的元素。例如,下面的代码将查找一个数组是否包含值为x的元素:

#include <iostream>
#include <algorithm>

using namespace std;
int main()
{
    int array[] = {2,5,1,67,34,21,78};
    int len = sizeof(array) / sizeof(int);
    int x;

    cout << "input x:"; cin >> x;
    sort(array,array + len) ; //排序
    
    auto k = lower_bound(array, array + len, x) - array;  //其值至少为x的第一个数组元素的下标位置
    if(k < len && array[k] == x) 
        cout << x << " find!" << endl;   
    else
        cout << x << "not find!" << endl;


    return 0;
    
}

可以通过lower_bound和upper_bound计算元素值x的个数。

#include <iostream>
#include <algorithm>

using namespace std;
int main()
{
    int array[] = {2,5,1,67,2,21,78};
    int len = sizeof(array) / sizeof(int);
    int x;

    cout << "input x:"; cin >> x;
    sort(array,array + len) ; //排序
    
    auto i = lower_bound(array, array + len, x) ;  //其值至少为x的第一个数组元素的地址
    auto j = upper_bound(array,array +len, x) ;  //返回一个指向其值大于x的第一个数组元素的地址
    cout << x << "个数:" << j - i << endl;

    return 0;
    
}

也可以使用 equal_range计算元素值x的个数。

#include <iostream>
#include <algorithm>

using namespace std;
int main()
{
    int array[] = {2,5,1,67,2,21,78};
    int len = sizeof(array) / sizeof(int);
    int x;

    cout << "input x:"; cin >> x;
    sort(array,array + len) ; //排序
    
    auto y = equal_range(array, array + len, x);
    cout << x << "个数:" << y.second -y.first << endl;

    return 0;
    
}