二分查找
用于有序数组的数据快速查找。注意无序数组无法用二分查找。
基本思想
类似昨天实现的猜数字游戏中人类的思考过程。猜数字的范围在1~100之间,那么首先猜50,然后按照被猜测值与猜测值之间的大小关系再确定下一个猜测数字。如果被猜测数字比50大,那么猜75,如果小,猜25。如此反复直到猜中。可以看到,每次猜测都可以去掉当前范围内一半的数字,这种方法对于有序的数组查找非常有用。
实现
假设有一个数组:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
需要输入一个数字n
,然后在数组中查找这个数字。如果数组中有和输入数字一样的值,返回这个值的下标,否则就返回未找到。
这里会涉及3个变量:left
、right
、mid
。开始的时候,left
为数组第一个元素的下标,right
为最后一个元素的下标,mid
为中间元素的下标(如果数组是偶数,居中的两个中的任何一个都可以)。首先使用arr[mid]
与数字n
做比较。如果n>arr[mid]
,说明数字在 arr[mid]
右边, 那么此时left = mid+1
(相当于数字范围的下限被加大了)。如果n<arr[mid]
,说明数字在 arr[mid]
左边, 那么此时right = mid-1
(相当于数字范围的上限被缩小了)。然后新的mid = left+(right-left)/2
。这样继续下去,直到n == arr[mid]
或者一直没有找到。
看一下代码:
int binary_search(int arr[], int sz, int n)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (n > arr[mid])
{
left = mid + 1;
}
else if (n < arr[mid])
{
right = mid - 1;
}
else
return mid+1;
}
return 0;
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int n = 0;
while (scanf("%d", &n) != EOF)
{
int ret = binary_search(arr, sz, n);
if (ret)
{
printf("找到了,下标是%d\n", ret-1);
}
else
{
printf("没找到\n");
}
}
return 0;
}
写这段代码的时候,碰到一个小bug。如果输入数字1,会打印没找到。实际上1对应的是arr[0]
。之前写的binary_search
返回值是mid
,如果mid == 0
,对于下面的判断就会出问题了。所以修改为返回mid+1
,打印为ret-1
。
函数
叫函数不便理解,其英文是function,意思就是功能 。所以函数就是实现某个功能的代码。
函数的基本语法
return_type function_name(para_type para1,para_type para2, para_type para3...)
{
function body;
}
形象一些的:
int Add(int x,int y)
{
int sum = 0;
sum = x+y;
return sum;
}
一个函数需要有返回类型、函数名、函数体。一个函数可以没有参数,可以没有返回值(对应返回类型为void)。
库函数和自定义函数
库函数在printf和scanf里有详细介绍,这里不再赘述。主要说说自定义函数。自定义函数是用户自己定义的,实现某个功能的函数。例如上面的代码中,Add
就是一个自定义函数。这个函数的返回类型是int
,函数名是Add
,参数是x
和 y
。关于自定义函数,需要注意:
- 返回值与返回类型必须匹配;
- 参数类型与实际传进来的参数必须匹配,否则会有隐式类型转换;
- 没有返回值的函数的返回类型是
void
;
形参与实参
形参是实参的一份复制,形参仅在函数被调用的时候在内存中开辟空间,否则就是一段代码。函数被调用时,改变形参的值不会改变实参的值。举个例子:
void test(int x,int y)
{
x = 0;
y = 0;
}
int main()
{
int a = 10;
int b = 20;
test(a,b);
}
这里执行了test函数之后实际不改变a和b的值。
return的功能
return
是函数的返回值,执行到return
之后函数跳出,不再执行return
之后的语句。如果函数中有分支语句,需要保证任何情况下都有返回,否则报错。
函数的嵌套和链式访问
函数嵌套是说在一个函数内部调用其他函数。链式访问是指一个函数的返回值作为另一个函数的参数被传入。
函数嵌套
先看一段代码:
void test();
{
printf("%d\n",100);
}
int main()
{
test();
return 0;
}
这个是在test函数内部嵌套了printf函数。
链式访问
看一下代码:
printf("%d",printf("%d",printf("%d",43)));
这段代码会在屏幕上打印:4321
。这是因为,printf
的返回值是其在屏幕上打印字符的个数。最里层的printf
打印了43
,然后返回值2作为中间层的printf参数,打印了2
之后,中间层函数的返回值又会作为最外层的参数,最后打印就是4321
。