前言
在之前的两篇文章中,我们总结了八种经典的排序算法,分别为直接插入排序,希尔排序,简单选择排序,堆排序,冒泡排序,归并排序,快速排序,基数排序,并分别用python和C++来实现每个排序算法。在这篇文章中,我们将继续学习数据结构中的三种常用的查找算法:二分查找,块查找,哈希查找。
二分查找
二分查找作为最简单的一种查找方式,已经被很多人应用。二分查找所查找的对象必须是一个有序数组,即如果一开始查找的是无序数组,首先要进行排序时候才能实行二分查找。
假如我们要查找一个升序数组里面的某一个数,二分查找就是将查找的键和子数组的中间键作比较,如果被查找的键小于中间键,就在左子数组继续查找;如果大于中间键,就在右子数组中查找,否则中间键就是要找的元素。
代码实现:
python版本:
def binary_research(List,key):
left = List[0]
right = len(List)-1
while(left<=right):
mid = left + (right - left) // 2
if List[mid]==key:
return mid
elif List[mid]<key:
left = mid + 1
elif List[mid]>key:
right = mid - 1
return -1
if __name__ == "__main__":
array = [3, 7, 8, 16, 17, 20, 33]
pos = binary_research(array,16)
print("要查找的数在 %d 索引处:"%(pos))
C++版本:
int binary_Search(int arr[], int length, int key){
int left =0;
int right = length-1;
while(left<=right){
int mid = (left+right)/2;
if(arr[mid]==key){
return mid;
}
else if(arr[mid]>key){
right = mid-1; //左侧查找
}
else{
left=mid+1; //右侧查找
}
}
return -1;
}
块查找
有时候,可能会遇到这样的表:整个表中的元素未必有序,但若划分为若干块后,每一块中的所有元素均小于(或大于)其后面块中的所有元素。我们称这种为分块有序。
首先,我们需要先建立一个索引表,索引表中为每一块都设置–索引项,每一个索引项都包含两个内容:
- 该块的起始地址
-
该块中最大(或最小)的元素
显然,索引表是按关键字递增或递减次序排列的。如下图所示:
在前面建立的索引表的基础上,我们查找一个关键字需要两个步骤:
- 在索引表中查找,目的是找出关键所属的块的位置。这里如果索引表较大的话,可以采用折半查找。
- 进入该块中,使用简单顺序表查找算法进行关键字查找。
代码实现:
#include<iostream>
using namespace std;
struct index_table
{
int maxNum;
int start; //表中每一块的起始位置
}indexTable[3];
int block_research(int a[], int key) {
int i = 0, j;
while (i<3 && key>indexTable[i].maxNum)
i++;
if (i < 3) {
j = indexTable[i].start;
for (; j < j + 2; j++) {
if (key == a[j])
return j;
}
return -1;
}
return -1;
}
int main() {
int a[6] = { 6, 8, 17, 11, 27, 21 };
int index = 0;
//将索引表分成3块,每块两个元素
for (int i = 0; i < 3; i++) {
indexTable[i].start = index;
int max = a[index];
for (int j = 1; j < 2; j++) {
if (max < a[index + j])
max = a[index + j];
}
indexTable[i].maxNum = max;
index += 2;
}
int pos = block_research(a, 11);
cout << pos;
return 0;
}
哈希查找
哈希技术是在记录的存储位置和记录的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。查找时,根据这个确定的对应关系找到给定值的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。 哈希技术既是一种存储方法,也是一种查找方法。
哈希表也是一种数据结构,理想情况下可以不需要任何比较,一次存取便能得到所查记录。所以它的优点就是查找特定记录的速度快。但因为哈希表是基于数组的,所以创建后就难于扩展,而且不利于遍历数据。
c中散列表使用struct结构实现,包含两个元素,一个是int型的amount,表示当前散列表中包含的元素个数; 一个是type* 类型的arr,用来实际存放数据;
构建散列表之前需要定义散列函数,定义 key和存储地址之间的映射关系,根据映射结果,把key存放到对应的arr下标位置上。
查找的时候,直接使用key和散列函数,对应出在散列表中的位置信息,从arr中取出即可。
#include<stdio.h>
#include<stdlib.h>
#define HASHSIZE 12
#define NULLKEY -32768
typedef struct
{
int *elem;
int count;
}HashTable;
int m=0;
//初始化散列表
int InitHashTable(HashTable *H)
{
int i;
m=HASHSIZE;
H->count=m;
H->elem=(int*)malloc(m*sizeof(int));
for(i=0;i<m;i++)
H->elem[i]=NULLKEY;
return 1;
}
//散列函数,定义 key和存储地址之间的对应关系
int Hash(int key)
{
return key%m;
}
//插入关键字进入散列表
void InsertHash(HashTable *H,int key)
{
int addr=Hash(key);
while(H->elem[addr]!=NULLKEY)
addr=(addr+1)%m;
H->elem[addr]=key;
}
//散列表查找关键字
int SearchHash(HashTable H,int key,int *addr)
{
*addr=Hash(key);
while(H.elem[*addr]!=key)
{
*addr=(*addr+1)%m;
if(H.elem[*addr]==NULLKEY||*addr==Hash(key))
{
return -1;
}
}
return *addr;
}
int main()
{
int a[12]={12,67,56,16,25,37,22,29,15,47,48,34};
HashTable H;
int i;
InitHashTable(&H);
for(i=0;i<m;i++)
InsertHash(&H,a[i]);
printf("插入之后的哈希表为:");
for(i=0;i<m;i++)
printf("%d,",H.elem[i]);
int addr,j;
j=SearchHash(H,a[5],&addr);
printf("搜索到 %d 在散列表中的地址是:%d",a[5],j);
}
PS:参考博客