顺序查找法
原理
从序列的一端开始遍历到另一端,找到对应元素则返回对应下标(或做其他处理),找不到则返回-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)。
缺点
只能找到首个要查找的元素出现的位置,数组的剩余部分不再遍历,没有办法知道剩余部分是否还有待查找元素。
哨兵查找法
原理
- 选取头部(尾部)位置做哨兵
- 从尾部(头部)开始遍历到头部(尾部)
- 找到对应元素返回对应下标
代码
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的操作,直到要查找的数字和某一个子序列的中间元素一样大,此时返回该位置的下标。
代码
二分查找要求我们首先对数组进行排序,我们通过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最大为。
所以时间复杂度为。
分块查找法
原理
前提:分好的块之间必须是有序的,块之间的元素可以无序。
- 建立索引表{当前块最大元素,当前块第一个元素的索引};
- 通过对有序排列的块用二分查找找到需要查询元素所在的块
- 缺点该块的上下界限
- 在该块内使用顺序查找需要查找的元素
- 如果查询到了,返回该位置处的索引
- 如果没查到,则代表整个数组中都没有该元素,返回-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()到O(n)之间。