• 递归和分治思想(折半查找)


    1、递归:(归去来兮)

    递归效率较低,如果明确知道迭代次数,则能用迭代最好用迭代,递归是函数自己调用自身,每次调用都需要入栈等操作。但是递归操作要比迭代简单和清楚。

    递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

    使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

    解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

    尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

    尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

    遗憾的是,大多数编程语言没有针对尾递归做优化。

    递归需要遵守的重要规则:

    1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间);

    2)方法的局部变量是独立的,不会相互影响;

    3)如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据;

    4)递归必须向退出递归的条件逼近,否则就会是无限递归(导致栈溢出);

    5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

    递归用于解决什么样的问题:

    1)各种数学问题:8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子问题等;(回溯,如果不能则会返回继续寻找)

    2)各种算法中,例如快速排序,归并排序,二分查找,分治算法等;

    3)将用栈解决的问题,利用递归代码会更加简洁。

    2、斐波那契数列的递归实现

    #include <stdio.h>
    int Fib(int i);
    int main()
    {
        int i;
        printf_s("请输入一个大于零的整数
    ");
        scanf_s("%d", &i);
        for (; i >= 0; i--)
        {
            printf_s("%d ", Fib(i));
        }
        
        return 0;
    }
    
    int Fib(int i)
    {
        if (i < 2)
            return i == 0 ? 0 : 1;
        return Fib(i - 1) + Fib(i - 2);
    }

    1)递归定义至少有一个终止条件,函数不再调用自身,开始返回。

    2)递归和迭代的区别:迭代使用的是循环结构,递归使用的是选择结构。但大量的递归调用会建立函数的副本,会消耗大量的时间和内存,而迭代不需要这种付出。

    3)递归函数分为调用阶段和回退阶段,递归的回退顺序是它调用顺序的逆序。

    举例:打印输入字符的倒序输出:

    void print()
    {
        char a;
        scanf_s("%c", &a);
        if (a != '#') print();
        if (a != '#') printf_s("%c", a);
    }

    3、分治思想

    采取各个击破,分而治之的原则。当一个问题规模较大且不容易求解时,可以考虑将其分为几个小的模块,逐一解决。采用分治思想处理问题时,其各个小模块通常具有与大问题相同的结构。

    4、折半查找

    折半查找法,是一种常用的查找方法,该方法通过不断缩小一半查找的范围,直到达到目的,所以效率比较高

    前提:针对有序数组(元素从小到大或从大到小),优点是查找速度比较快,时间复杂度为O(log2n)。

     迭代实现:

    int main()
    {
        int a[11] = { 1, 3, 3, 10, 13, 16, 19, 21, 23, 27, 31 };
        int left, mid, right,num;
        left = 0;
        right = 10;
        num = 27;
    
        while (left <= right)
        {
            mid = (left + right) / 2;
            if (a[mid] > num)
                right = mid - 1;
            else if (a[mid] < num)
                left = mid + 1;
            else
                break;
        }
        printf("index:%d", mid);
        system("pause");
        return 0;
    }

    递归实现

    int binary_search(int arr[], int left, int right,int ele)
    {
        int mid = (left + right) / 2;  //边界条件是找到当前值,或者查找范围为空。否则每一次查找都将范围缩小一半。
       if(left>right)
       {
         return -1;  
    }
      else
    {
      if (arr[mid] > ele)
            right = mid - 1;
        else if (arr[mid] < ele)
            left = mid + 1;
        else
            return mid;
        return binary_search(arr, left, right, ele);

    }
    } int main() { int a[11] = { 1, 3, 3, 10, 13, 16, 19, 21, 23, 27, 31 }; int left, right, num, index; left = 0; right = 10; num = 27; index = binary_search(a, left, right, num); printf("index:%d", index); system("pause"); return 0; }

    上面讲的递归的二分查找法就是一个分治算法的典型例子,分治算法常常是一个方法,在这个方法中含有两个对自身的递归调用,分别对应于问题的两个部分

    二分查找中,将查找范围分成比查找值大的一部分和比查找值小的一部分,每次递归调用只会有一个部分执行。

  • 相关阅读:
    初学java-基础
    初学java-基础
    HYPER-V 实现管理器远程管理虚拟机
    zeromq rpc原型
    haproxy+keepalived
    [转]序列化悍将Protobuf-Net,入门动手实录
    RabbitMQ的几种应用场景
    redis数据类型及使用场景
    [转]DDD领域驱动设计基本理论知识总结
    Centos 下编译安装Redis
  • 原文地址:https://www.cnblogs.com/lemonzhang/p/12380968.html
Copyright © 2020-2023  润新知