C语言静态查找表:顺序查找、二分查找、分块查找

目录

1 静态查找表

2 静态查找的三种方法

2.1 顺序查找

2.1.1 概念

2.1.2 分类

2.1.3 源代码示例

2.1.4 性能分析

2.2 二分查找

2.2.1 实现算法前提

2.2.2 实现思路(以升序为例)

2.2.3 源代码示例

2.2.4 性能分析

2.3 分块查找

2.3.1 概念

2.3.2 实现思路

2.3.3 特点

2.3.4 源代码示例

2.3.5 性能分析

3 三种算法性能比较

 


1 静态查找表

概念:静态查找表是数据元素的线性表,可以是基于数组的顺序存储或以线性链表存储。

2 静态查找的三种方法

2.1 顺序查找

2.1.1 概念

顺序查找又称静态查找,是最基本的查找方法之一。

2.1.2 分类

1.无监视哨:从表中一端开始,向另一端逐个将其关键码与待查找元素比较。若相等,则查找成功,返回给定值在表中存储位置;否则查找失败。

2.有监视哨:表中 0 位置存储待查找元素。从表尾向表头查找,若搜索到 0 位置,说明查找失败;否则查找成功。优点:查找过程中省略了每次查找的检测过程,直接返回元素下标,当 L.length >= 1000 时,与方法(1)相比进行一次查找所需平均时间几乎减少一半。

ps:监视哨也可设置在高下标处

2.1.3 源代码示例

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100   // 表中最大元素个数
typedef int ElemType; // 设置表中元素类型为整型
typedef struct {
    ElemType data[MAXSIZE + 1]; // 下标0处存放监视哨,数组元素存储1~n单元
    int length;
} SqList;

void CreateSqList(SqList *L);         // 创建表
int SeqSearch1(SqList L, ElemType x); // 无监视哨查找算法
int SeqSearch2(SqList L, ElemType x); // 有监视哨查找算法

int main(void)
{
    SqList L;
    int index;

    CreateSqList(&L);

    printf("元素总数为:%d\n", L.length);

    index = SeqSearch1(L, 3);
    if (index == 0)
        printf("查找失败!\n");
    else
        printf("查找成功,下标为:%d\n", index);

    index = SeqSearch2(L, 11);
    if (index == 0)
        printf("查找失败!\n");
    else
        printf("查找成功,下标为:%d\n", index);

    return 0;
}

void CreateSqList(SqList *L) // 创建表
{
    int i, n;

    printf("请输入元素个数:");
    scanf("%d", &n);
    while (n > MAXSIZE || n < 1) {
        printf("个数超出范围,重新输入:");
        scanf("%d", &n);
    }
    L->length = n;

    printf("请依次输入元素值:\n");
    for (i = 1; i <= L->length; i++)
        scanf("%d", &(L->data[i]));
}
int SeqSearch1(SqList L, ElemType x) // 无监视哨查找算法
{
    int i = 1;

    /*
     * for (i = 1; i <= L.length; i++)
     *     if (L.data[i] == x)
     *         return i;
     */

    while (i <= L.length && L.data[i] != x)
        i++;
    if (i <= L.length)
        return i;
    return 0;
}
int SeqSearch2(SqList L, ElemType x) // 有监视哨查找算法
{
    int i;
    L.data[0] = x;
    for (i = L.length; L.data[i] != x; i--)
        ;
    return i;
}

2.1.4 性能分析

设表中有 n 个元素,给定值 x 与表中第 i 个元素相等,则定位元素 x 时,共查找 n - i + 1 次,即 Ci = n - i + 1。因此查找成功时,平均查找长度(Average search length)为:

ASL=\sum^n_{i=1}p_i(n-i+1)

 

设每个元素的查找概率相等,即 p_i=\frac{1}{n}

 

将 \large p_{i} 代入可得:ASL=\frac{n+1}{2}

 

2.2 二分查找

2.2.1 实现算法前提

要实现表的二分查找,必须使表有序(升序 / 降序)

2.2.2 实现思路(以升序为例)

取有序表中间元素为比较对象:

(1)若待查找元素 = 中间元素,则返回中间元素下标;

(2)若待查找元素 < 中间元素,则在表左半部分查找;

(3)若待查找元素 > 中间元素,则在表右半部分查找

2.2.3 源代码示例

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100   // 表中最大元素个数
typedef int ElemType; // 设置表中元素类型为整型
typedef struct {
    ElemType data[MAXSIZE + 1]; // 下标0处存放监视哨,数组元素存储1~n单元
    int length;
} SqList;

void CreateSqList(SqList *L);           // 创建表
void ASCSort(SqList *L);                // 对表进行升序排序
int BinarySearch(SqList L, ElemType x); // 二分查找非递归算法
int BinarySearch_Recursive(SqList L, int low, int high, ElemType x);
// 二分查找递归算法

int main(void)
{
    SqList L;
    int index;

    CreateSqList(&L);
    printf("元素总数为:%d\n", L.length);
    ASCSort(&L);

    index = BinarySearch(L, 3);
    if (index == 0)
        printf("查找失败!\n");
    else
        printf("查找成功,下标为:%d\n", index);

    index = BinarySearch_Recursive(L, 1, L.length, 8);
    if (index == 0)
        printf("查找失败!\n");
    else
        printf("查找成功,下标为:%d\n", index);

    return 0;
}

void CreateSqList(SqList *L) // 创建表
{
    int i, n;

    printf("请输入元素个数:");
    scanf("%d", &n);
    while (n > MAXSIZE || n < 1) {
        printf("个数超出范围,重新输入:");
        scanf("%d", &n);
    }
    L->length = n;

    printf("请依次输入元素值:\n");
    for (i = 1; i <= L->length; i++)
        scanf("%d", &(L->data[i]));
}
void ASCSort(SqList *L) // 对表进行升序排序
{
    int i, j, temp;

    for (i = 1; i <= L->length - 1; i++)
        for (j = i; j <= L->length; j++)
            if (L->data[i] > L->data[j]) {
                temp = L->data[i];
                L->data[i] = L->data[j];
                L->data[j] = temp;
            }

    printf("升序排序后:");
    for (i = 1; i <= L->length; i++)
        printf("%d, ", L->data[i]);
    printf("\n");
}
int BinarySearch(SqList L, ElemType x) // 二分查找非递归算法
{
    int low, mid, high;

    low = 1;
    high = L.length;
    while (low <= high) {
        mid = (high + low) / 2;

        if (x == L.data[mid])
            return mid;
        else if (x < L.data[mid])
            high = mid - 1;
        else // (x > L.data[mid])
            low = mid + 1;
    }

    return 0;
}
int BinarySearch_Recursive(SqList L, int low, int high, ElemType x)
{
    int mid;

    if (low > high)
        return 0;
    else {
        mid = (high + low) / 2;

        if (x == L.data[mid])
            return mid;
        else if (x < L.data[mid])
            return BinarySearch_Recursive(L, low, mid - 1, x);
        else // (x > L.data[mid])
            return BinarySearch_Recursive(L, mid + 1, high, x);
    }
}

2.2.4性能分析

 

 

 

查找图中任一元素的过程,即是从根节点到该元素的路径,比较次数 k 为该元素节点在树中的层次数。所以:K=\lfloor log_2n+1 \rfloor

设每个元素的查找概率相等,即 

p_i=\frac{1}{n}

ASL=\sum^n_{i=1}p_iC_i=p_ik \approx log_2(n+1)-1

 

2.3分块查找

2.3.1概念

分块查找又称索引顺序查找,是对顺序查找的一种改进。

2.3.2实现思路

将数据表分成若干块,然后为每块建立一个索引,将所有块组织起来建立一个索引表

(1)数据表:存储所有数据元素

(2)索引表(index_table):表中每个索引有两个部分,关键码字段、指针字段

关键码字段:存储对应块中的最大元素

指针字段:存储对应块中起始元素位置

2.3.3特点

表中数据是分块有序的。即索引表中数据是有序的,数据表中数据不要求有序。

查找元素时,先根据索引表确定元素所在块数,然后再从数据表对应块中查找该元素。

2.3.4源代码示例

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE  100  // 表中最大元素个数
#define BLOCKNUM 5    // 索引表中最大块数
typedef int KeyType;  // 设置索引表元素类型为整型
typedef int DataType; // 设置记录的其他项为整型
typedef struct        // 数据表结构体
{
    KeyType key;       // 节点元素
    DataType data;     // 节点的其他项
} SqList[MAXSIZE + 1]; // 数据表0位置存储数据表的长度

typedef struct // 索引表结构体
{
    KeyType key;            // 块的最大关键字
    int addr;               // 块的起始地址
} IndexTable[BLOCKNUM + 1]; // 索引表0位置存储数据表的长度

void CreateSqList(SqList L, IndexTable id);   // 创建表
void TraverseSqList(SqList L, IndexTable id); // 遍历表
int BlockSearch(SqList L, IndexTable id, KeyType x); // 查找块中元素值为x的元素

int main(void)
{
    SqList L;
    IndexTable id;
    int index;

    CreateSqList(L, id);
    TraverseSqList(L, id);

    index = BlockSearch(L, id, -7);
    if (index == 0)
        printf("表中无此元素!\n");
    else
        printf("查找成功,元素下标为:%d\n", index);

    index = BlockSearch(L, id, 17);
    if (index == 0)
        printf("表中无此元素!\n");
    else
        printf("查找成功,元素下标为:%d\n", index);

    index = BlockSearch(L, id, 100);
    if (index == 0)
        printf("表中无此元素!\n");
    else
        printf("查找成功,元素下标为:%d\n", index);

    return 0;
}

void CreateSqList(SqList L, IndexTable id) // 创建表
{
    int i, j, k, m, n;
    int elemnum, bnum; // 总元素个数;总块数
    int max = INT_MIN; // 块中最大元素

    printf("请输入元素个数( < 100)、块数( < 5 ):");
    scanf("%d%d", &elemnum, &bnum);
    while (elemnum < 1 || elemnum > MAXSIZE || bnum < 1 || bnum > BLOCKNUM ||
           bnum > elemnum) {
        printf("输入有误,重新输入:");
        scanf("%d%d", &elemnum, &bnum);
    }

    L[0].key = elemnum; // 数据表0位置存储数据表的长度
    id[0].key = bnum;   // 索引表0位置存储索引表的长度

    n = elemnum / bnum;             // 平均每块元素个数
    for (i = 1; i <= bnum - 1; i++) // 构造前 bnum - 1 块中的元素
    {
        printf("请输入块 %d 的 %d 个元素(最小元素不能小于 %d ):", i, n, max);

        k = (i - 1) * n; // 第 i 块元素起始下标
        for (j = 1; j <= n; j++) {
            scanf("%d", &(L[k + j].key));
            if (max < L[k + j].key) // 获取第 i 块中最大元素
                max = L[k + j].key;
        }
        id[i].key = max;
        id[i].addr = k + 1;
    }

    k = (bnum - 1) * n;
    m = elemnum - k; // 最后一块元素个数
    printf("请输入块 %d 的 %d 个元素(最小元素不能小于 %d ):", bnum, m, max);
    for (j = 1; j <= m; j++) // 构造最后一块中的元素
    {
        scanf("%d", &(L[k + j].key));
        if (max < L[k + j].key) // 获取最后一块中最大元素
            max = L[k + j].key;
    }
    id[i].key = max;
    id[i].addr = k + 1;
}
void TraverseSqList(SqList L, IndexTable id) // 遍历表
{
    int i;

    printf("---------数据表信息---------\n");
    for (i = 1; i <= L[0].key; i++) // 遍历并输出表中元素
        printf("%d, ", L[i].key);
    printf("\n---------索引表信息---------");
    for (i = 1; i <= id[0].key; i++) // 遍历并输出索引表信息
        printf("\n%d, %d", id[i].key, id[i].addr);
    printf("\n----------------------------\n");
}
int BlockSearch(SqList L, IndexTable id, KeyType x) // 查找块中元素值为x的元素
{
    int low1, high1, low2, high2, mid, i;

    low1 = 1;
    high1 = id[0].key;    // 设置索引表二分查找上下区间
    while (low1 <= high1) // 查找元素所在块数
    {
        mid = (low1 + high1) / 2;
        if (x <= id[mid].key)
            high1 = mid - 1;
        else
            low1 = mid + 1;
    }

    if (low1 <= id[0].key) // 若low1 > id[0].key,则关键子大于数据表中所有元素
    {
        low2 = id[low1].addr; // 元素所在块的最小下标
        if (low1 == id[0].key)
            high2 = L[0].key; // 元素所在块的最大下标
        else
            high2 = id[low1 + 1].addr - 1;

        for (i = low2; i <= high2; i++) // 在指定块中查找元素并返回下标
            if (L[i].key == x)
                return i;
    }

    return 0; // 查找失败返回0
}

2.3.5性能分析

设 n 个数据元素的表分为 m 个子表,则子表(sub_table)元素个数为 t = n / m ;

ASL=ASL_{(index\_table)}+ASL_{(sub\_table)}=\frac{1}{2} \times (m+1) +\frac{1}{2} \times (\frac{n}{m}+1)=\frac{1}{2} \times (m+\frac{n}{m})+1

m= \sqrt{n} 时,  ASL=\sqrt{n}+1 达到最小值。

3.三种算法性能比较

顺序查找时间复杂度:T(n)=O(n)

二分查找时间复杂度:T(n)=O(log_2n)

分块查找时间复杂度:T(n)=O(\sqrt{n})\\

O(n) > O(\sqrt{n})>O(log_2n)

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值