海洋馆目录🐋🐳🐟🐬🦈
🐬 今天咱们的主题是对数组中数据的查找方法:二分查找和线性查找,那么闲话少说,我们直奔主题。
🐬对于我们常见到的数组中的数据一般分为以下两种 1.有序数组 2.无序数组 对于这两种数组数据的数据查找形式还各不相同,那么我们依次深入,逐个击破。我们先从最普遍的无序数组下手。
🐋对于无序数组数据的查找——线性查找
🐟线性查找的优势
🐬线性查找也称为顺序查找,算法简单直观,但效率较低。对于无需的数据而言要么将其进行排序转换成为有序数组,要么就只能使用线性查找进行数组数据的处理。这就线性查找的优势——可以直接对无序数组进行查找。
🐟线性查找原理
🐬线性查找是针对每个数组数据进行遍历处理。也就是将你需要查找的数据和数组中的每一个数据进行对比,如果相同,那么就返回数组中该元素的下标,如果不相同那么就依次向后接着查找。可以用一张图片通俗易懂的进行表达:
🐬就像我们上面图片所依次操作的一样,数据12对数组中的数据从下标0开始向后依次尝试判断。等到数据相等时则跳出循环。但是很显然这样的操作 效率很低。最差的情况需要将数组的所有元素都查找一遍才可以输出结果,但是这又不妨是一种可以尝试的方法。下面我们将线性查找的思想转化为代码呈现给大家。
🐟程序运行效果:
🐟代码呈现:
#include<stdio.h>
//查找数组中指定元素
//返回元素下标
//写法一:函数参数为指针的形式
//int FindNum(int* arr, int sz,int n)
//{
// int i = 0;
// for (i = 0; i < sz; i++)
// {
// if (*(arr+i) == n)
// {
// return i + 1;
// }
// }
// return -1;
//}
//查找数组中指定元素
//返回元素下标
//写法二:函数参数为数组的形式
int FindNum(int arr[], int sz, int n)
{
int i = 0;
for (i = 0; i < sz; i++)
{
if (arr[i] == n)
{
return i + 1;
}
}
return -1;
}
int main()
{
int n = 0; //要查找的数为n
scanf("%d", &n);
int arr[] = { 12,34,6,56,22,78,99,83,2,45 };
int sz = sizeof(arr) / sizeof(arr[0]); //数组元素个数(最多查找次数)
int ret=FindNum(arr, sz,n);
if (ret != -1)
{
printf("找到了,数据%d位于数组的第%d个元素位置处", n, ret);
}
else
{
printf("该数组不存在该元素。");
}
return 0;
}
🐬就像上面所表示的一样,我们的线性查找的介绍也已经结束了,是不是很简单?接下来就让我们进入有序数组的世界来体会一下二分查找的奇妙之处。
🐳2.对于有序数组数据的查找——二分查找
🐟二分查找的优势
🐬二分查找也叫“折半查找”,二分查找的优点就是效率高,查找方便。科普一个小知识,你知道从1000个数据中进行二分查找需要多少次吗?——只需要10次。 从100万个数据中进行二分查找需要多少次呢?我想你肯定想不到——只需要20次。甚至从10亿个数据中查找一个数字也只需要30此而已!!!但是二分查找也有一个弊端,那就是只可以从有序数组总进行数据的查找。但是没关系,数组排序只需要进行一次就好了,但是数组查找可以重复进行。
🐟二分查找的原理
🐬顾名思义二分查找的原理是对于整个数组进行这种处理。
🐟二分查找步骤:
🐬STEP 1 : 确定数组左右下标,分别定义为left和right,left=0,right=数组元素个数-1
🐬STEP 2 : 计算mid的值 mid=(left+right) / 2
🐬STEP 3 : 利用arr[mid]和查找数据进行比较
(1)arr[mid]<要查找的数据,将left赋值成mid+1,right不变
(2)arr[mid]>要查找的数据,将right赋值成mid-1,left不变
(3)arr[mid]==要查找的数据,直接return返回mid的值
🐬STEP 4 : 结束条件
(1)找到数据的值 (2)left>right (数组中没有符合元素)
🐬经过上面的步骤循环往复就可以最终找到我们想要查找的数据了,文字总是抽象的,那么接下来我们就利用图像来进一步了解二分查找的原理。
🐬上面就是我们二分查找的主要思想。那么接下来,我们将上面的思想,付出实践,转换成代码的形式来呈现给大家。
🐟程序运行效果:
🐟代码呈现:
#include<stdio.h>
//二分查找
//返回元素位置
int BinFinde(int* arr, int right, int n)
{
int left = 0, mid = 0;
do
{
mid = (left + right) / 2;
if (*(arr + mid) == n)
{
return mid + 1;
}
else if (*(arr + mid) < n)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
} while (left<=right);
return -1;
}
int main()
{
int arr[] = { 1,6,8,12,17,23,43,47,58,79,93 };
int n = 0; //要查找的元素
scanf("%d", &n);
int right = sizeof(arr) / sizeof(arr[0])-1; //求数组元素个数-1就是right
int ret=BinFinde(arr, right, n);
if (ret != -1)
{
printf("找到了,数据%d位于数组的第%d个元素位置处。", n, ret);
}
else
{
printf("该数组不存在该元素。");
}
return 0;
}
🐬那么上面我们对二分查找的介绍以及使用也就介绍完了,但是其中还有一个细节值得注意。
🦈二分查找细节完善——“致命的危险”
🐟二分法数据溢出的解决方法
🐬虽然上面的程序看起来天衣无缝,但是真的是这样吗?作为一名合格的程序员需要考虑到所有可能发生的情况,这并非吹毛求疵,鸡蛋里挑骨头。如果数组长度很大很大,使得 left 和 right 之和超出int的界限,那么在执行 (left+right) / 2 的时候就有可能发生数据溢出,导致 mid 成为负数,最终是结果错误。那么我们应该怎样解决这个问题呢?这时候我们就需要把 mid = ( left + right ) / 2 这个条件改为 mid = left + ( right - left ) / 2 。(对于int类型数据溢出的问题详细解说也很复杂,请参考本人主页博客的数据存储——char类型的界限,在这里我们来简单地用图像进行解释。)在下面我们来解释一下这种操作的原理。
🐟二分法数据溢出的原因
🐬由于我们的数据分为有符号数以及无符号数,通常没有特定标号的数据都为有符号数。unsigned 类型为无符号数,无符号数所表示的最大值的大小是有符号数最大值大小的两倍,所以无符号数所表示的最大值为有符号数的一个循环周期。用图像表示也就是下图的形式。
🐬通过上图我们可以大致了解超出一定的范围 int 数据会出现负数的原因,(详情请参考本人char类型的解析哈)我们可以利用实际数据进行举例说明 mid = left + ( right - left ) / 2 这样表达不会出现复数的原因。同样如下图表示:
🐬使用 mid = left + ( right - left ) / 2 的优点以及合理的解释相信大家通过上副图片已经体会到了。那么,到了该说再见的时候了,感谢大家的观看,祝大家天天开心。