二分查找的递归和非递归

查找算法

常见的查找算法大概有顺序查找、二分查找、二叉排序树查找、哈希表法(散列表)、分块查找等,
下面简单了解一下其他几种查找算法。

1.顺序查找

也就是暴力方法,按顺序比较每个元素,直到找到关键字为止。
条件:无序或有序数据,时间复杂度:O(n)

2.二叉排序树查找

二叉排序树的性质:
1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3. 它的左、右子树也分别为二叉排序树。

在二叉查找树b中查找x的过程为:
1. 若b是空树,则搜索失败,否则:
2. 若x等于b的根节点的数据域之值,则查找成功;否则:
3. 若x小于b的根节点的数据域之值,则搜索左子树;否则:
4. 查找右子树。

时间复杂度:O(\log_2(n))  

3.哈希表查找

创建哈希表(散列表)
哈希查找的操作步骤:⑴用给定的哈希函数构造哈希表⑵根据选择的冲突处理方法解决地址冲突⑶在哈希表的基础上执行哈希查找。
建立哈希表操作步骤: ① 取数据元素的关键字key,计算其哈希 函数值。若该地址对应的存储 空间还没有被占用,则将该元素存入;否则执行step2解决冲突。 ② 根据选择的冲突处理方法,计算关键字 key的下一个存储地址。若下一个存储地 址仍被占用,则继续执行step2,直到找 到能用的存储地址为止。 
时间复杂度:几乎是O(1),取决于产生冲突的多少。

4.分块查找

将n个数据元素"按块有序"划分为m块(m ≤ n)。
每一块中的结点不必有序,但块与块之间必须"按块有序";
即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;
而第2块中任一元素又都必须小于第3块中的任一元素,……。
然后使用二分查找及顺序查找。

二分查找

一般是操作有序数组,查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;
如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。

这种搜索算法每一次比较都使搜索范围缩小一半。时间复杂度:O(logn)。

二分查找的应用

二分查找一般是用在有序序列的查找,很多时候查找需要结合排序操作来进行。
但是需要注意,二分查找并不一定非要在有序序列中才能得到应用,
只要在二分之后可以淘汰掉一半数据的场景,都可以应用二分搜索。

递归和非递归实现


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

public static int binSearch(int[] arr,int des){

         

        if(arr==null || arr.length<1){

            return -1;

        }

         

        int left=0;

        int right=arr.length-1;//注意防止数组下标越界

        /**

         * 这里的判断条件必须包含等于,

         * 考虑{1,2,3},查找1,如果不判断等于,就丢失了比较导致查找错误

         */

        while(left<=right){

            int mid=left+(right-left)/2;

            if(des==arr[mid]){

                return mid;

            }else if(des<arr[mid]){//舍弃右侧

                /**

                 * 注意,此处的mid已经参与过比较了并失败了,

                 * 所以重新二分不要包含进来,下同

                 */

                right=mid-1;

            }else{//舍弃左侧

                left=mid+1;

            }

        }

        return -1;//查找失败 返回-1

    }

     

    /**

     * @Title: bSearchRecursion

     * 递归参数有不同,left和right明显在每次递归时都是变的

     */

    public static int binSearchRecursion(int[] arr,int left,int right,int des){

         

        if(arr==null || arr.length<1){

            return -1;

        }

         

        if(left<=right){

            int mid=left+(right-left)/2;

            if(des==arr[mid]){

                return mid;

            }else if(des<arr[mid]){//舍弃右侧

                return binSearchRecursion(arr,left,mid-1,des);

            }else{//舍弃左侧

                return binSearchRecursion(arr,mid+1,right,des);

            }

        }

        return -1;

             

    }

  

二分查找和斐波那契查找

折半查找,一般将待比较的key值与第mid=(low+high)/2位置的元素比较,
同时为了防止溢出,使用更高效的移位,也可以记做 mid=low+((low+high)>>1)。
比较结果分三种情况
1)相等,mid位置的元素即为所求
2)> ,low=mid+1;
3)< ,high=mid-1;

斐波那契查找要求元素表中记录的个数为某个斐波那契数减1,即n=Fk-1;
斐波那契数列:0、1、1、2、3、5、8、13、21、……
如果设F(n)为该数列的第n项(n∈N)。那么这句话可以写成如下形式:
F(0) = 0,F(1)=1,F(n)=F(n-1)+F(n-2) (n≥2),
这是一个线性递推数列,斐波那契数列的算法实现经常在数据结构教材的递归一节中出现。

对于二分查找,分割是从mid=(low+high)/2开始;而对于斐波那契查找,分割是从mid = low + F[k-1] - 1开始的; 通过上面知道了,数组a现在的元素个数为F[k]-1个,即数组长为F[k]-1,mid把数组分成了左右两部分, 左边的长度为:F[k-1] - 1, 那么右边的长度就为(数组长-左边的长度-1), 即:(F[k]-1) - (F[k-1] - 1) = F[k] - F[k-1] - 1 = F[k-2] - 1。

斐波那契查找的核心是:
1)当key=a[mid]时,查找成功;
2)当key<a[mid]时,新的查找范围是第low个到第mid-1个,此时范围个数为F[k-1] - 1个,即数组左边的长度,所以要在[low, F[k - 1] - 1]范围内查找;
3)当key>a[mid]时,新的查找范围是第mid+1个到第high个,此时范围个数为F[k-2] - 1个,即数组右边的长度,所以要在[F[k - 2] - 1]范围内查找。

与二分查找相比,斐波那契查找它只涉及加法和减法运算,而不用除法(用“>>1”要好点)。因为除法比加减法要慢,在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。

 

时间: 2024-10-29 07:06:43

二分查找的递归和非递归的相关文章

求二分查找的递归和非递归的时间空间效率比较

问题描述 求二分查找的递归和非递归的时间空间效率比较 求二分查找的递归和非递归的时间空间效率比较,为什么在刘汝佳的书上说,一般用非递归方法 解决方案 递归算法写起来简单,但是有两个不足,一个是调用接口的开销,函数调用本身是有开销的.另一个是堆栈内存比较小,递归调用层次深,容易引起堆栈溢出错误(著名的stack overflow).非递归没有这个问题.还有就是一些编程语言(很久很久以前,在你出生的年代之前),是没有函数调用的,那么就不能递归了. 解决方案二: 递归牵涉到环境保存和环境恢复操作,因此

全排列的递归与非递归实现浅析

全排列问题在公司笔试的时候很常见,这里介绍其递归与非递归实现. 递归算法 1.算法简述 简单地说:就是第一个数分别以后面的数进行交换E.g:E = (a , b , c),则 prem(E)= a.perm(b,c)+ b.perm(a,c)+ c.perm(a,b)然后a.perm(b,c)= ab.perm(c)+ ac.perm(b)= abc + acb.依次递归进行. void swap(string &pszStr,int k,int m) { if(k==m) return ; c

如何使用递归和非递归方式反转单向链表

以下是对使用递归和非递归方式反转单向链表的示例进行了详细的分析介绍,需要的朋友可以过来参考下   问题: 给一个单向链表,把它从头到尾反转过来.比如: a -> b -> c ->d 反过来就是 d -> c -> b -> a . 分析:假设每一个node的结构是: 复制代码 代码如下: class Node {  char value;  Node next; } 因 为在对链表进行反转的时候,需要更新每一个node的"next"值,但是,在更新

妹纸求助-c++编写寻找国都的算法,递归和非递归法

问题描述 c++编写寻找国都的算法,递归和非递归法 用c++编写寻找国都的代码 给出一个矩阵及一些国都名: o k d u b l i n dublin a l p g o c e v tokyo r a s m u s m b london o s l o n d o n rome y i b l g l r c bonn k r z u r i c h paris o a i b x m u z oslo t p q g l a m v lima 要求从这个矩阵中找出这些国都名,并输出它们的

递归算法-C:用递归及非递归解决迷宫问题

问题描述 C:用递归及非递归解决迷宫问题 以下是现有的代码,但是递归放在里面出现错误,求大神给我改改. #include #include #define N 39 #define M 39 int X; int maze[N+2][M+2]; /******递归函数定义*******/ typedef struct { int x,y; }Dj; Dj move[4]; /******非递归函数定义*******/ struct point{ int row,col,predecessor;

c++-1.用list实现stack。 2.递归变非递归。

问题描述 1.用list实现stack. 2.递归变非递归. 1.用list实现stack. template class Stack{ public: intsize() const {___________} voidpush(const T & x) {_________} constT & pop() {__________} private: vectorvt_list; } 麻烦解释一下这个代码,以及空白处应该填什么,谢谢. 2.递归变非递归. #include structN

C语言实现二叉树的常用的算法(递归与非递归实现遍历)

队列头文件: #include <stdio.h> #include "BinaryTree.h" // // 队列头文件:Queue.h #ifndef QUEUE_H #define QUEUE_H // // 队列最大元素个数 #define MAX_QUEUE_SIZE 10 typedef BTree QueueElemType; // // 队列结构体 typedef struct tagQueue { BTree *base; int front; // 头指

二叉树的遍历详解(前序中序后序层次-递归和非递归)

二叉树 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁.而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现.在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点 前序遍历 前序遍历按照"根结点-左孩子-右孩子"的顺序进行访问. 递归实现 void PreOrder(Tree

php实现无限级分类查询(递归、非递归)_php技巧

做PHP这么长时间,发现后台管理系统不可少的一个应用模块就是对栏目的分类,一般情况下栏目都要做成是无限级的,也就是说每个栏目理论上都可以添加子栏目.在我看来这种情况处理起来整体上说也不是很复杂,唯一一个相对来说较难的点是无限级栏目的查询. 下面就这种情况我来向大家做一个简单的介绍,对于这种无限级栏目的查询一般情况下有两种方式,其中一种就是使用栈的机制,另一种是使用递归函数的方式(当然递归函数实现机制也是借助于栈来实现的).就这两种方式下面我们分别介绍. 递归函数实现方式 上面提到,递归函数的也是