顺序查找(又称为线性查找)
顺序查找的线性表定义如下:
typedef struct
{
int key;
}Search;
顺序查找的算法思想:
将顺序表中数据元素存放在数组下标为1到n的位置上,而下标为0的位置空出来作为监视哨,在查找之前,先将要查找的关键字key存放到数组下标为0的位置上,然后从顺序表的最后一个元素开始,从后往前依次和key比较。最终返回数组中与key相等的元素的位置,若返回0,则表示查找失败;若返回非零的整数,则表示查找成功。
核心算法如下:
//顺序查找
int SeqSearch (Search a[], int n, int key)
{
a[0].key = key;
//用数组的a[0]元素存放关键字
while (a[n].key != key)
//从后往前查找
{
n--;
}
return n;
//返回该元素所在的数组的下标,若下标等于0,则表示表中无此关键字
}
顺序查找的源代码:
举例: 查找值为8的元素
# include <stdio.h>
# define MAX 10
typedef struct
{
int key;
}Search;
//顺序查找
int SeqSearch (Search a[], int n, int key)
{
a[0].key = key;
//用数组的a[0]元素存放关键字
while (a[n].key != key)
//从后往前查找
{
n--;
}
return n;
//返回该元素所在的数组的下标,若下标等于0,则表示表中无此关键字
}
int main (void)
{
Search a[MAX];
int n, i;
printf ("请输入元素的个数:");
scanf ("%d", &n);
for (i = 1; i < n+1; i++)
{
printf ("a[%d] = ", i);
scanf ("%d", &a[i].key);
}
i = SeqSearch (a, n+1, 8);
if (i == 0)
{
printf ("表中无此元素!!!");
}
else
{
printf ("该元素的位置为:%d", i);
}
}
运行结果:
折半查找(又称为二分查找)
使用折半查找的前提条件:
表中的关键字必须按照大小有序排序
折半查找的算法思想:
取中间元素作为比较对象,若要查找的元素与中间元素相等,则查找成功;若小于中间元素,则到中间元素的左半区继续查找;若大于中间元素,则到中间元素的右半区继续查找。不断重复上述过程,直到查找成功或查找失败。
折半查找的主要步骤:
(1)初始化查找范围,令low=1,high=n
(2)求查找的中间项:mid=(low+high)/2
(3)将要查找的关键字和中间项作比较:
- 若相等,则查找成功,此时mid指向的位置就是要查找元素的位置
- 若k小于关键字,则低位指针low不变,将高位指针high改为mid-1
- 若k大于关键字,则高位指针high不变,将低位指针low改为mid+1
(4)重复步骤(2)和(3),直到查找成功或查找失败(即low>high)
(5)若查找成功,则返回要查找元素的位置,即当前指针mid;否则返回查找失败标志0
其算法如下:
//二分查找(折半查找)
int BinSearch (Search a[], int n, int key)
{
int low = 1, high = n;
int mid;
while (high >= low)
{
mid = (low + high) / 2;
if (a[mid].key == key)
{
return mid;
}
else if (a[mid].key > key)
{
high = mid-1;
}
else
{
low = mid + 1;
}
}
if (low>high)
{
return 0;
}
}
查找过程举例:
对于关键字序列{2,4,6,9,10,13,18},采用折半查找法查找6和17的具体过程。
(1)查找key=6的过程。
初始时令low=1,high=7,则mid=4
此时因为key=6 < a[mid]=9,所以high=mid-1=3, mid=(low+high)/2=2
此时因为key=6 > a[mid]=4,所以low=mid+1=3, mid=(low+high)/2=3
因为此时key=a[mid]=6,所以查找成功,返回mid的值,即返回要查找元素的位置
(1)查找key=17的过程。
初始时令low=1,high=7,则mid=4
此时因为key=17 > a[mid]=9,所以low=mid+1=5, mid=(low+high)/2=6
此时因为key=17 > a[mid]=13,所以low=mid+1=7, mid=(low+high)/2=7
此时因为key=17 < a[mid]=18,所以high=mid-1=6, mid=(low+high)/2=6
因为此时high=6 < low=7,所以查找失败,返回失败标志0
分块查找的源代码:
举例:查找值为6的元素
# include <stdio.h>
# define MAX 10
typedef struct
{
int key;
}Search;
//二分查找(折半查找)
int BinSearch (Search a[], int n, int key)
{
int low = 1, high = n;
int mid;
while (high >= low)
{
mid = (low + high) / 2;
if (a[mid].key == key)
{
return mid;
}
else if (a[mid].key > key)
{
high = mid-1;
}
else
{
low = mid + 1;
}
}
if (low>high)
{
return 0;
}
}
int main (void)
{
Search a[MAX];
int n, i;
printf ("请输入元素的个数:");
scanf ("%d", &n);
for (i = 1; i < n+1; i++)
{
printf ("a[%d] = ", i);
scanf ("%d", &a[i].key);
}
i = BinSearch (a, n+1, 6);
if (i == 0)
{
printf ("该表中无此元素!!!");
}
else
{
printf ("该元素的位置为:%d", i);
}
}
运行结果:
分块查找(又称为索引顺序查找)
使用分块查找的前提条件:
将查找表分成若干块,每块中的元素存储顺序都是任意的,但块与块之间必须按照关键字的大小有序排列,即前一块的最大关键字要小于后一块中的最小关键字。
基本准备:
建立一个索引表,索引表的每一项对应线性表的一块,索引项由关键字域和指针域组成,关键字域存放相应块的最大关键字,指针域存放指向块内的第一个元素和最后一个元素的数组下标值。
分块查找的线性表类型与顺序查找的类型相同,但是还需要定义一个索引表类型
//索引表类型
typedef struct
{
int low, high, key;
//low表示块内低地址,high表示块内高地址,key表示块内最大关键字
}TypeIdx;
typedef struct
{
int key;
}Search;
我们可以设计一个数组a来存放关键字的信息,设计一个数组b来存放索引表的信息
举例:对于关键字序列{6,12,7,10,9,22,15,20,18,26},我们可以将其分为两块,即{6,12,7,10,9}和{22,15,20,18,26},前一块的最大关键字12小于后一块的最小关键字15
分块查找的算法思想:
先采用折半查找法确定待查找元素属于哪一块(由于索引表是递增有序的,所以采用折半查找速度较快),再采用顺序查找法查找块内的元素。(由于每块内的元素个数少,所以对执行速度没有太大的影响)
因为索引表需要根据线性表中的个元素的值来生成,所以为了生成索引表,要编写建立索引表的函数,其算法如下:
//建立索引表
void CreateIdx (Search a[], TypeIdx b[], int m, int n)
//m表示元素的总数,n表示每块中的元素个数
{
int i, j, k, max;
//用i表示a数组的下标值,k表示b数组的下标值
for (i = 0, k = 0; i < m; i = i+n, k++)
//用for循环将每块的最大关键字,低地址和高地址都确定下来
{
max = a[i].key;
//先将最大值设为第i个元素
for (j = i+1; j < i+n && j < m; j++)
{
if (a[j].key > max)
{
max = a[j].key;
}
}
b[k].key = max;
//确定该块的最大值元素
b[k].low = i;
//确定该块的低地址
if (i+n <= m)
//当最后一块的长度小于n时
{
b[k].high = i+n-1;
}
else
{
b[k].high = m-1;
}
//确定该块的高地址
}
}
在线性表中查找关键字为k的元素。若找到,则返回它所在的位置;否则返回-1,其算法如下:
int BlkSearch (int f, Search a[], TypeIdx b[], int k)
//f是索引表的元素个数,k是要查找的关键字
{
int i, j, mid;
int low = 0, high = f;
while (low <= high)
{
mid = (low + high)/2;
if (b[mid].key > k)
{
high = mid-1;
}
else if (b[mid].key < k)
{
low = mid+1;
}
else
{
low = mid;
break;
}
}
//用二分法找到要查找元素的所在块
if (low < f)
{
i = b[low].low;
j = b[low].high;
//将该块低,高地址分别赋值给i,j
}
while (i <= j && a[i].key != k)
{
i++;
}
if (i > j)
{
return -1;
}
else
{
return i;
//i表示要查找元素的位置
}
}
分块查找的源代码:
举例:对于关键字序列{6,12,7,10,9,22,15,20,18,26},我们来查找关键字为9的元素
# include <stdio.h>
# include <math.h>
# define MAX 100
//索引表类型
typedef struct
{
int low, high, key;
//low表示块内低地址,high表示块内高地址,key表示块内最大关键字
}TypeIdx;
typedef struct
{
int key;
}Search;
//建立索引表
void CreateIdx (Search a[], TypeIdx b[], int m, int n)
//m表示元素的总数,n表示每块中的元素个数
{
int i, j, k, max;
//用i表示a数组的下标值,k表示b数组的下标值
for (i = 0, k = 0; i < m; i = i+n, k++)
//用for循环将每块的最大关键字,低地址和高地址都确定下来
{
max = a[i].key;
//先将最大值设为第i个元素
for (j = i+1; j < i+n && j < m; j++)
{
if (a[j].key > max)
{
max = a[j].key;
}
}
b[k].key = max;
//确定该块的最大值元素
b[k].low = i;
//确定该块的低地址
if (i+n <= m)
//当最后一块的长度小于n时
{
b[k].high = i+n-1;
}
else
{
b[k].high = m-1;
}
//确定该块的高地址
}
}
//打印索引表
void DisIdx (int m, int n, Search a[], TypeIdx b[])
{
int i;
printf ("建立的顺序表为:\n");
for (i = 0; i < m; i++)
{
printf ("%d ", a[i].key);
}
CreateIdx (a, b, m, n);
printf ("\n索引表信息如下:\n");
printf ("低地址 高地址 最大关键字\n");
for (i = 0; i < m/n; i++)
{
printf (" %d %d %d\n", b[i].low, b[i].high, b[i].key);
}
}
int BlkSearch (int f, Search a[], TypeIdx b[], int k)
//f是索引表的元素个数,k是要查找的关键字
{
int i, j, mid;
int low = 0, high = f;
while (low <= high)
{
mid = (low + high)/2;
if (b[mid].key > k)
{
high = mid-1;
}
else if (b[mid].key < k)
{
low = mid+1;
}
else
{
low = mid;
break;
}
}
//用二分法找到要查找元素的所在块
if (low < f)
{
i = b[low].low;
j = b[low].high;
//将该块低,高地址分别赋值给i,j
}
while (i <= j && a[i].key != k)
{
i++;
}
if (i > j)
{
return -1;
}
else
{
return i;
//i表示要查找元素的位置
}
}
int main (void)
{
Search a[MAX];
TypeIdx b[MAX];
int m, i, n, f;
printf ("请输入元素的个数:");
scanf ("%d", &m);
printf ("请依次输入各个元素(要求块内无序,块间有序)\n");
for (i = 0; i < m; i++)
{
scanf ("%d", &a[i].key);
}
printf ("\n请输入索引表每块的大小为:");
scanf ("%d", &n);
DisIdx (m, n, a, b);
f = ceil (m/n);
//f为大于或等于m/n的最小整数
i = BlkSearch (f, a, b, 9);
if (i == -1)
{
printf ("\n表中无此元素!!!");
}
else
{
printf ("\n该元素在表中的位置为:%d", i);
}
}
运行结果:
三种查找算法的比较
优点 | 缺点 | 平均性能 | |
---|---|---|---|
顺序查找 | 对表中的数据如何存储没有要求 | 平均查找长度较大,效率低 | 差 |
折半查找 | 比较次数少,查找速度快 | 要求待查找的表必须为有序表,而且排序会很费时 | 好 |
分块查找 | 只要找到相应的块,插入或删除元素很容易 | 要求待查找的表,块与块之间必须按关键字的大小有序排列 | 介于二者之间 |