机试指南第二章-经典入门-查找例题自解

Posted yun-an

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机试指南第二章-经典入门-查找例题自解相关的知识,希望对你有一定的参考价值。

查找:

对于查找问题,有难有易。可能只是直接地对某个数字的查找,也可能涉及搜索等相对难度更大的算法。这里先介绍查找的基础概念和方法。

例 2.9 找 x

AC代码:

#include<cstring>
#include<iostream>

using namespace std;
int num[205];

int main()

    int n, m, x;
    memset(num, 0, sizeof(num));
    while (cin >> n)
    
        bool flag = false;
        for (int i = 0; i < n; i++)
        
            cin >> num[i];
        
        cin >> x;
        for (int i = 0; i < n; i++)
        
            if (num[i] == x)
            
                cout << i << endl;
                flag = true;
                break;
            
        
        if (!flag)cout << -1 << endl;
    
    return 0;

通过此例,我们可以了解一下查找所涉及的几个基本要素。

1.查找空间。也常被称为解空间。所谓查找,就是在该查找空间中找寻符合我们要求的解的过程。在此例中,整个数组包含的整数集就是查找空间。

2.查找目标。我们需要一个目标来判断查找空间中的各个元素是否符合我们的要求,以便判断查找活动是否已经成功。在此例中,即数组中的数字与目标数字是否相同。

3.查找方法。即利用某种特定的策略在查找空间中查找各个元素。不同的策略对查找的效率和结果有不同的影响,所以对于某个特定的问题,我们要选择切实可行的策略来查找解空间,以期事半功倍。在此题中,查找方法即线性地遍历数组。

二分查找 :

二分查找建立在待查找元素排列有序的前提上,例如在一个升序有序的数组中查询某元素。我们以在有序表1,3,4,5,6,8,10查找3为例,了解它的查找过程:

1.将查找开始点设为第一个数组元素(1),结束点设为最后一个数组元素(10), 即查找子集为整个搜索空间1,3,4,5,6,8,10。

2.然后将起始点和结束点正中间的数与查找目标进行比较,若该中间数字等于目标数字则查找成功,查找结束;若大于查找目标,则说明查找目标只可能存在于查找子集中以该中间数字为界的较小的一半中,则移动查找结束点为该中间数字的前一个数字,即新的查找子集为旧的查找子集中以中间数字为界的较小的一半;若小于查找目标,则相应的得到新的查找子集为旧查找子集中以中间数字为界的较大的一半。在该例中,即目标数字3小于中间数字5,移动查找结束点至中间点(5)的前一个元素(4),新的查找子集为1,3,4,然后继续步骤2。

3.若在查找过程中出现查找起始点大于查找结束点的情况,则说明查找子集已经为空集,查找失败。否则继续步骤2的查找过程。 

用二分查找查找长度为 L 的有序数组,时间复杂度可由原本线性查找的O(L)降低到O(logL)。 

例 2.10 查找学生信息 

AC代码:

#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;

struct Student

    char id[100];//学号
    char name[100];//姓名
    int age;
    char sex[5];//性别
    bool operator < (const Student & A)//方便sort函数排序
    
        return strcmp(id, A.id) < 0;
    
stu[1000];

int main()

    int n;
    while (cin >> n)
    
        for (int i = 0; i < n; i++)
        
            cin >> stu[i].id >> stu[i].name >> stu[i].sex >> stu[i].age;
        
        sort(stu, stu + n);
        int t;
        cin >> t;//需要查找t组数据
        while (t-- != 0)
        
            int ans = -1;//初始化目标下标为-1
            char x[30];
            cin >> x;//待查找学号
            int beg = 0, end = n - 1;//查找起始点和结束点,初始查找子集为整个数组
            while (end >= beg)//查找子集不为空时重复二分查找
            
                int mid = (beg + end) / 2;//中间点下标
                int tmp = strcmp(stu[mid].id, x);
                if (tmp == 0)
                
                    ans = mid;
                    break;
                
                else if (tmp > 0) end = mid - 1;
                else beg = mid + 1;
            
            if (ans == -1) cout << "No Answer!" << endl;
            else cout << stu[ans].id << " " << stu[ans].name << " " << stu[ans].sex << " " << stu[ans].age << endl;
        
    
    return 0;

利用二分查找,原本O(n * m)的时间复杂度被优化到O(nlogn(排序) + m * logn),而该复杂度是符合我们要求的。 

在查找某特定元素是否存在以外,二分查找还有另一类非常重要的运用,即定界。思考如下问题,在一个升序有序的数组中,确定一个下标点,使在这个下标点之前(包括该下标点)的数字均小于等于目标数字(该目标数字一定大于等于数组中最小的数字),而数组的其余部分均大于目标数字,我们该如何编写程序。这里给出代码以供参考。 

//存在一个升序有序的数组buf,其大小为size,目标数字为target 
int beg = 0 , end = size; //初始情况与二分查找一致 
while (beg <= end)  //二分循环条件与二分查找一致   
    int mid = (beg + end) / 2;   
    if (buf[mid] <= target) beg = mid + 1; //符合前一部分数字规定   
    else end = mid - 1; //否则 

int ans = end; //最后,end即为我们要求的数字数组下标,buf[end]为该数字本身 

 

以上是关于机试指南第二章-经典入门-查找例题自解的主要内容,如果未能解决你的问题,请参考以下文章

机试指南第六章-搜索-例题自解

-经典入门-贪心例题自解

《N诺机试指南》查找贪心链表问题

算法竞赛入门经典训练指南pdf高清版免费下载

算法竞赛入门经典 例题 3-4 回文串

《算法竞赛入门经典》例题5.4.1