静态查找表(顺序查找,折半查找,分块查找)

顺序查找(又称为线性查找)

顺序查找的线性表定义如下:

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);
	}
}

运行结果:
在这里插入图片描述

三种查找算法的比较

优点 缺点 平均性能
顺序查找 对表中的数据如何存储没有要求 平均查找长度较大,效率低
折半查找 比较次数少,查找速度快 要求待查找的表必须为有序表,而且排序会很费时
分块查找 只要找到相应的块,插入或删除元素很容易 要求待查找的表,块与块之间必须按关键字的大小有序排列 介于二者之间

猜你喜欢

转载自blog.csdn.net/qq_52607834/article/details/117396879
今日推荐