数组查找算法详解

顺序查找法

原理

从序列的一端开始遍历到另一端,找到对应元素则返回对应下标(或做其他处理),找不到则返回-1。

代码

int search(int a[],int length,int key){
	for(int i = 0;i<length;i++){
		if(a[i] == key){
			return i;
		}
	}
	return -1;
}
int main(){
	int a[] = {9,2,4,3,5,6,7,8};
	int length = sizeof(a)/sizeof(a[0]);
	int key = 6;//k为要查找的内容 
	int result = search(a,length,key);
	printf("%d",result);
	return 0;
}

时间复杂度

只用了一个for循环将数组遍历所以时间复杂度为O(n)。

缺点

只能找到首个要查找的元素出现的位置,数组的剩余部分不再遍历,没有办法知道剩余部分是否还有待查找元素。

哨兵查找法

原理

  1. 选取头部(尾部)位置做哨兵
  2. 从尾部(头部)开始遍历到头部(尾部)
  3. 找到对应元素返回对应下标

代码

int search(int a[],int length,int key){
	if(a[0] == key){//判断数组头部第一个元素和哨兵即待查找元素是否相等 
		return 0;
	} else{
		a[0] = key;//将哨兵放到数组头部 
	}
	while(a[0] != a[--length]){
		//在倒叙遍历数组时,找到与a[0]相等的元素则退出循环 
	}
	if(length == 0){//如果遍历到了哨兵位置即a[0]才找到,说明该数组中没有该元素,返回-1 
		return -1;
	} 
}
int main(){
	int a[] = {9,2,4,3,5,6,7,8};
	int length = sizeof(a)/sizeof(a[0]);
	int key = 6;//k为要查找的内容 
	int result = search(a,length,key);
	printf("%d",result);
	return 0;
} 

时间复杂度

与顺序查找一样只用了一个while循环将数组遍历所以时间复杂度为O(n)。

缺点

如果将哨兵放在数组最后一个位置,那么需要顺序遍历数组,则只能找到首个要查找的元素出现的位置,数组的剩余部分不再遍历,没有办法知道剩余部分是否还有待查找元素。

如果将哨兵放在数组第一个一个位置,那么需要倒叙遍历数组,则只能找到最后一个要查找的元素出现的位置,数组的剩余部分不再遍历,没有办法知道剩余部分是否还有待查找元素。

二分查找法

原理

     前提:序列有序

  1. 拿要查找的数字和数组中间位置比较,判断比中间位置大还是小;
  2. 如果大,则和原数组右半部分的中间位置比较,判断比中间位置大还是小;如果小,则和原数组左半部分的中间位置比较,判断比中间位置大还是小;
  3. 循环1、2的操作,直到要查找的数字和某一个子序列的中间元素一样大,此时返回该位置的下标。

代码

二分查找要求我们首先对数组进行排序,我们通过qsort以及cmp_int函数对数组实现排序。查找函数我们可以用两种方法来实现,下面使用的是递归的方法

//1.排序函数 
int cmp_int(const void* elem1, const void* elem2)
{
	return *(int*)elem1 - *(int*)elem2;
}

//2.查找函数(递归)
int search(int left,int right,int key,int a[]){
	if(left<=right){
		int mid = (left + right)/2;
		if(a[mid] == key){
			return mid;
		}
		if(a[mid] > key){
			return search(left,mid-1,key,a);
		}
		if(a[mid] <key){
			return search(mid+1,right,key,a);
		}
	}else{
		return 0;
	}
}
int main(){
	int a[] = {9,2,4,3,5,6,7,8};
	int length = sizeof(a)/sizeof(a[0]);
	int key = 6;//k为要查找的内容 
	//给a数组排序 
	qsort(a, length, sizeof(a[0]), cmp_int);
	//此时a数组为有序数组,可以开始二分查找 
	for(int i =0;i<length;i++){
		printf("%d,",a[i]);
	}
	int left = 0;
	int right = length-1;
	int result = search(left,right,key,a);
	printf("\n%d",result);
	return 0;
} 

下面用的是while循环的方法,两组代码只有search函数不同其他都一样

//查找函数(循环)
int search(int left,int right,int key,int a[]){
	while(left<=right){
		int mid = left + (right - left) / 2;
		if(key < a[mid]){
			//如果key比中间值小,更新右指针为中间值-1,下一次循环在当前中间值的左边进行比较 
			right = mid - 1;
		}else if(key > a[mid]){
			//如果key比中间值大,更新左指针为中间值+1,下一次循环在当前中间值的右边进行比较 
			left = mid + 1; 
		}else{
			//此时表示找到key = a[mid],返回当前mid值即可。 
			return mid;
		}
	}
	//此时表示没找到 
	return -1;

} 

时间复杂度

  • 假设数组有n个元素
  • 第一次,在n个元素内查找目标元素;
  • 第二次,在n/2个元素内查找目标元素;
  • 第三次,在n/(2^2)个元素内查找目标元素;
  • ……
  • 第k次,在n/(2^k)个元素内查找目标元素。

因为2^k <= n,所以当且仅当n/(2^k) >= 1时,k最大为\log_{2}n

所以时间复杂度为\log_{2}n

分块查找法

原理

前提:分好的块之间必须是有序的,块之间的元素可以无序。

  1. 建立索引表{当前块最大元素,当前块第一个元素的索引};
  2. 通过对有序排列的块用二分查找找到需要查询元素所在的块
  3. 缺点该块的上下界限
  4. 在该块内使用顺序查找需要查找的元素
  5. 如果查询到了,返回该位置处的索引
  6. 如果没查到,则代表整个数组中都没有该元素,返回-1

代码

typedef struct ST{
	int key;
	int index;
}ST;

int blockSearch(int arr[],int length,ST* st,int len_st,int key){//len_st代表st的长度 
	//1.块是有序的,先用二分查找找到key对应的块
	int left = 0,right = len_st - 1,mid = left + (right - left) / 2; 
	
	while(left < right){
		if(key < st[mid].key){
			right = mid;
		}else if(key > st[mid].key){
			left = mid + 1;
		}else break;
		mid = left + (right - left) / 2;
	}
	int begin = st[mid].index;
	int end; 
	if(mid == len_st - 1){
		//如果是最后一个块,该块的结束位置是数组长度
		end = length; 
	}else{
		//否则,该块的结束位置是下一个块开始的位置。 
		end = st[mid + 1].index;
	} 
	//2.在key对应的块中,通过顺序查找判断有没有key
	while(begin < end){
		if(key == arr[begin]){
			//如果查找到就返回对应下标
			return begin;
		}
		begin++;
	}
	//如果没有就返回-1表示没有找到 
	return -1; 
}
int main(){
	int arr[20] = {8,1,4,6,9,12,15,13,16,22,27,26,25,20,33,34,35,32,46,40};//元素分块要满足块与块之间有序,块内元素可无序。
	int length = sizeof(arr)/sizeof(arr[0]);
	ST st[5] = {
		//分组操作{每一块最大元素,每一块第一个元素索引} 
		{9,0},
		{16,5},
		{27,9},
		{35,14},
		{46,18} 
	};
	int key = 46;
	int result = blockSearch(arr,length,st,5,key);
	printf("%d",result);
	return 0;
} 

时间复杂度

由于分块查找时分块的长度不固定,索引表的长度也不固定,查找索引表时使用的方法也可能不固定,因此分块查找的时间复杂度也不固定。

O(\log_{2}n)到O(n)之间。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值