从有序数组中查找数字的4种方法/二分查找/二分查找的函数递归/一一对比/一一对比的递归

目录

情境设置:

第一种方案:一一对比

函数递归:

函数递归有两个必要条件:

第二种方案一一对比的进阶版--函数递归

图解:

第三种方案:二分查找

二分查找主要的流程

图解

第四种方案二分查找进阶方案--函数递归

图解

总结:


 

情境设置:

从一组有序数组中1,2,3,4,5,6,7,8,9,10中找数字。

第一种方案:一一对比

Step1,将有序的数字放到数组中

Step2,设置循环,一个一个的与要找的数进行比较。

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};//将有序的数字放到数组中。
	int i = 0;//要查找的数字
	int n = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//求数组中元素个数
	scanf("%d", &i);
	for (n = 0; n < sz; n++)
	{
		if (arr[n] == i)
		{
			printf("找到了,下标为%d", n);
			break;
		}
	}
    //没找到的时候下标为sz-1
	if (n > sz-1)
		printf("找不到");
	return 0;
}

 

函数递归:

所谓递归,拆开理解,既存在递推,也存在回归,两个过程。

 

什么时候可以用函数递归呢?

 

在我看来,重复执行某一些相同或者类似的”动作“,可以考虑函数递归。

 

函数递归的中心思想就是大事化小。

 

个人理解:将我们重复执行的一些相同的或者类似的“动作”,写成函数,设置好限制条件,每一次递推进函数的时候,接近限制条件,最后不满足限制条件,结束递推,开始回归。回归到第一次调用我们函数的时候,递归才算结束。(有点像循环,但不一样)

函数递归有两个必要条件:

1.每一次递归之后,我们都要离递归结束的条件(限制条件)越来越近。

2.需要有一个限制条件,使我们的递归结束

第二种方案一一对比的进阶版--函数递归

我们来分析下:

 

重复动作:一个一个比较,我们一直在重复这个动作

 

限制条件的设置:我们什么时候会不比较了呢?要么是找到了,要么是没找到的时候。

 

我们来看代码

#include <stdio.h>
int search(int* pa,int k,int sz)//  pa是指针指向首元素(arr[0])的地址 
{
    //sz 是最大下标 也就是 9
	//比较的是  当前进入函数pa所指向的地址,与&arr[9](arr[9]的地址)比较 
    //pa+sz 一直指向的是 arr[9]的地址,下面有详细说明 
	
    //限制条件:每一次递归进来,判断有没有越界

	if ( pa <= (pa+sz) )  //对10个元素一一找到(下标从0-9)
	{
         //判断当前地址所指向的元素是不是与要找的k相同
		if (*pa != k)    
            //不是的话继续递归
		    //pa+1-->每递归一次,pa指向后面一个元素的地址  
		    //sz-1-->保证了每次递归之后 pa+sz 指向的都是arr[9]的地址
            //原因很简单,我们pa在递推过程中每次都向后一步,pa+sz也会后走,
            //为了让pa+sz指向&arr[9]保持不变,sz必须-1
			return 1 + search(pa + 1, k,sz-1);

		else 
            //当找到了,就返回0
			return 0;
	}
	else  
        //返回值可以是任意自然数,下图有说明
        //不可以是负数,结合主函数中if(ret>sz)条件来看的
		return 0;
}
int main()
{
	 int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	 int sz = sizeof(arr) / sizeof(arr[0]) - 1;
	 // sz-->数组的最大下标-->先求的是数组元素个数,再减一。
	 int k = 0;
     scanf("%d", &k);
	 int ret = search(arr,k,sz);
	 //参数分别为 数组首元素的地址 要找的数 数组的最大下标

     //ret 表示找到元素的下标 ,如果serch返回的是大于sz(9)的数,那么就是没找到
     //因为最大下标为9

	 if (ret > sz)
		 printf("找不到");
	 else
		 printf("找到了,下标为%d", ret);
	return 0;
}

图解:

①②...表示每一次递推的顺序

 

紫色的1,2,3,4...是返回的值

 

这里是k > 10 的情况

 

红色线代表递推出去,紫色线代表回归。

 

f4d6e27072d649cd97a6b4ab530a834b.png

 

 为什么ret接收的是10呢?(可以打开监视测试一下),我们search函数进去了11次, ,回去的时候也是11次。回去的时候呢,有10次是return 1+search() , 1次是 结束函数递归的时候,return 0 ; 

 

所谓递归,一去一回。从哪里进来的,回哪里去,每一次回归,都是回到 return 1+ search(pa+1,k,sz-1)的地方

第三种方案:二分查找

一一对法,太过繁琐,每一次都需要比较很多的值才能确定有没有我们要找的数字。

我想大家应该有玩过猜数字的游戏吧。1-100之间的数字,让你去猜,你为了缩小范围先猜50,在看看大了还是小了,在缩小一半,以此类推。

这就是我们的二分查找。

二分查找主要的流程

知左(右)下标,求中间下标,在比较。比较一次,没找到,看k(要找的数)是在中间数的左边还是右边,调整左下标或者右下标,再求中间下标,再比较。一直重复这个动作。直到有一次,我们左下标大于右下标,结束,或者找到k也会结束。

int mid_search(int arr[],int sz,int k)
{
     //最左边的下标
	int left = 0;
    //最右边的下标
	int right = sz - 1;

    //循环的限制条件  
    //在每一次找不到的时候,left或者right会发生改变,直到不满足条件为止,跳出循环
	while (left <= right)
	{
        //求中间元素的下标
		int mid = left + (right - left) / 2;
          
            //用中间元素与k进行比较 
			//中间值大,则说明k在中间值的左边
            //调整我们的最右边的下标
		if (arr[mid] > k)
			right = mid - 1;
        
           //中间值小,则说明k在中间值的右边
           //调整我们最左边的下标
		else if (arr[mid] < k)
			left = mid + 1;

        //找到了就会返回中间元素的下标
		else
			return mid;
	}

	//如果没找到,我们的 left 将会大于 right 跳出循环
    //返回一个sz
	return sz;
}
int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);//求出数组中元素的个数
	int k = 0;
	scanf("%d", &k);

	//函数参数分别为 数组首元素地址,元素个数,要找的数
	int ret = mid_search(arr,sz,k);
    //ret 表示找到元素的下标。因为数组下标从0开始,最大下标为[sz-1]
    //所以当没找到,返回sz,是可以判断找不到的

	if (ret>sz-1)
		printf("找不到");
	else
		printf("找到了,下标为%d",ret);
	return 0;
}

图解

图片带你深入理解二分查找

下标,标记的时候是从矩形左侧的边开始标记的。也就是说,要从矩形左边从0开始数。

 

4ccf6a42ae304b3a880daa0f145e9f67.png

 

d0ef9436ba344819be86f299b3793589.png

e6241b9db5b04761a60fc8cf81da973f.png

7d32326f67fd4fa9889e56c0c1f32ea1.png

 

第四种方案二分查找进阶方案--函数递归

int mid_search(int left, int right,int* pa,int  k)
{
	int mid = left + (right - left) / 2; //找中间的数的下标  
	if (left <= right)
	{
        //中间元素比k要小
		if (*(pa + mid ) < k) 
		{
			left = mid + 1;
			return mid_search(left, right, pa, k);
		}
       //中间元素比k要大
		else if (*(pa + mid ) > k)  
		{
			right = mid - 1;
			return mid_search(left, right, pa, k);
		}
        //找到了
		else
			return mid;
	}
   
    //找不到
    //返回一个大于sz-1的数,超过最大下标[sz-1],这里sz = 10(sz元素总个数)
    return 10;  
}
int main()
{
	 int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	 int k = 0;
	 int left = 0;
	 int right = sizeof(arr) / sizeof(arr[0]) - 1;//最大下标
     scanf("%d", &k);
	 int ret = mid_search(left,right,arr,k);
	 //参数为左下标,右下标,首元素地址,要查找的数

	 if (ret>right)
		 printf("找不到");
	 else
		 printf("找到了,下标为%d", ret);
	return 0;
}

图解

8359b1dd3127477699e3e34fbfecef64.png

2fa5ffd36b9f476284e430eacd46f0ba.png

b65b39723f044f6892fc904fcb06362d.png

 

d91d8efb7a8e4e9883d49b64bd15a990.png

如何返回?看下面图理解

 

bb2f28666bb1434096ab52cc3464a07d.png

 

 

 

总结:

我们讲了从有序数组中查找数字的4种方法。

 

一一对比和进阶版,二分查找和进阶版

 

让我们更深入了解函数递归。

 

函数递归,既有递推,又有回归。回归的时候,从哪里来回哪里去。

 

 

 

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值