C语言:查找算法

本人录制技术视频地址:https://edu.csdn.net/lecturer/1899 欢迎观看。

查找是指从一组记录集合中找出满足给定条件的记录。今天给大家介绍三种基本的查找算法: 顺序查找、折半查找和索引查找。

一、顺序查找

基本思想:

从查找表的一端开始,逐个将记录的关键字和给定的值进行比较,如果某个记录的关键字和给定值相等,则称查找成功;否则说明查找表中不存在关键值为给定值的记录,则称查找失败。

代码清单:

int sequenceSearch(int *a, int length, int k) {
    for (int i = 0; i < length; i++) {
        if (k == a[i]) {
            return i;
        }
    }
    return -1;
}

参数说明:

a: 表示给定的记录集合。

length: 表示给定记录集合的个数。

k: 表示给定值。

结论分析:

顺序查找的优点是算法简单,且对表的结构没有任何要求。它的缺点就是查询效率低,因此,当表中元素个数比较多时,不宜采用顺序查找。

二、折半查找(二分查找)

前提条件:

1. 要求查找表中的记录按关键字有序排列。

2. 只能适用于顺序存储结构。

基本思想:

先取查找表的中间位置的关键字与给定值作比较,若他们的值相等,则查找成功;如果给定值比该记录的关键字值大,说明要查找的记录一定在查找表的后半部分,则在查找表的后半分继续使用遮半查找;反之,在查找表的前半部分使用折半查找....... 直到查找成功,或者直到确定查找表中没有待查找的记录为止,即查找失败。

流程图演示:

C语言:查找算法_第1张图片

代码清单:

int binSearch(int *a, int length, int k) {
    int low = 0, high = length - 1, mid = 0, find = 0;
    while ((low <= high) && (!find)) {
        mid = (low + high) / 2;
        if (k == a[mid]) {
            find = 1;
            break;
        } else {
            if (k > a[mid]) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
    }
    
    if (find) {
        return mid;
    } else {
        return -1;
    }
}

参数说明:

a: 表示给定的记录集合。

length: 表示给定记录集合的个数。

k: 表示给定值。

结论分析:

折半查找要求查找表按关键字有序,而排序是一种很费事的运算;另外,折半查找要求表是顺序存储的,为保持表的有序性,在进行插入和删除操作时,都必须移动大量记录。因此,折半查找的高查找效率是以牺牲排序为代价的,它特别适合于一经建立就很少移动、而又经常需要查找的线性表。

三、索引查找

基本思想:

索引查找又称为分块查找,即把线性表分成若干块,在每一块中记录的关键字不一定有序,但是块与块之间必须有序。假设这种排序是按关键字值递增排序的,抽取各块中的最大关键字及该块的起始位置构成索引表,按块的顺序存放在一个数组中,显然这个数组是有序的,一般按升序排列。文字描述有点晦涩,示意图如下:

C语言:查找算法_第2张图片


其中,索引表很明显是一个结构体,它的定义如下:

struct idtable {
    int key;
    int address;
    int length;
};

key: 索引表中的关键字。

address: 索引表对应块的首地址。

length: 索引表对应块的长度。

从上面的示意图我们可以看出,索引查找,我们可以先对索引表进行折半查找,找出对应的块,然后再在对应的快中利用顺序查找找出需要查找的关键字。

代码清单:

struct idtable {
    int key;
    int address;
    int length;
};

int indexSearch(int *a, int a_length, struct idtable idtables[], int idtables_length, int keyword) {
    // 先用二分查找法找出对应的块号
    int low1 = 0, high1 = idtables_length - 1, mid = 0;
    while (low1 <= high1) {
        mid = (low1 + high1) / 2;
        if (keyword <= idtables[mid].key) {
            high1 = mid - 1;
        } else {
            low1 = mid + 1;  // 查找完毕,low1存放块号
        }
    }
    
    if (low1 < idtables_length) {
        int index = -1;
        // low2为块在表中的起始地址
        int low2 = idtables[low1].address, high2 = 0;
        if (low1 == idtables_length - 1) {
            high2 = a_length - 1;
        } else {
            high2 = idtables[low1 + 1].address - 1;
        }
        
        // 用顺序查找法在对应的块中找出符合条件的值
        for (int i = low2; i <= high2; i++) {
            if (a[i] == keyword) {
                index = i;
                break;
            }
        }
        
        return index;
    } else {
        return -1;
    }
}

int main(int argc, const char * argv[]) {
    
    // 主表
    int a[] = {22, 12, 13, 8, 9, 20, 33, 42, 44, 38, 24, 46, 60, 58, 74, 47, 86, 53};
    // 索引表
    struct idtable idtables[3] = {
        {22, 0, 6},
        {46, 6, 6},
        {86, 12, 6}
    };
    
    int result = indexSearch(a, 18, idtables, 3, 74);
    
    printf("index:%d", result);
    
    return 0;
}

结论分析:

索引查找的效率介于顺序查找和折半查找之间,对于数据量巨大的线性表,它是一种较好的方法。在表中插入或者删除一个记录时,只要找到该记录所属的块,就可以在该块内进行插入和删除运算,插入和删除无需移动大量记录。分块查找的主要代价:需要增加一个辅助数组的存储空间和将初始表分块排序的运算。

你可能感兴趣的:(C语言)