【算法基础(2)】二分查找和斐波那契数列

  1. 二分查找
    二分查找高效的前提是数据结构是有序的
    思路

    1.排序
    2.将数组折半,分成左右两个数组。
    3.判断要查找的数和中间位置数值的大小,来判断要查找的数实在哪一半。
    4.之后继续折半查找,直至找到这个数。

    图解

    首先是找出各个指针;

    在这里插入图片描述

    此时target>arr[mid],所以left=mid+1,然后更新arr[mid];

    在这里插入图片描述

    此时target<arr[mid],所以right=mid_1,然后更新arr[mid];

    在这里插入图片描述
    最后发现arr[mid]=target,结束

    function binarySearch(arr, target){
          
          
    
        let start = 0;
        let end = arr.length - 1;
        if(!end){
          
          
          return arr[0] === target ? 0 : -1;		
        }
        if(end == 1){
          
          
            return arr[0] === target ? 0 : arr[1] === target ? 1 : -1; 
        }
        let middle;
        while(start <= end){
          
          
            middle = start + ((end - start) >> 1) | 0; 
            if(arr[middle] === target){
          
          
                return middle
            }else if(target > arr[middle]){
          
          
                start = middle + 1
            }else{
          
          
                end = middle - 1
            }
        }
        return -1
    }
    
  2. Master公式计算递归的时间复杂度

  3. 有些算法在处理一个较大规模的问题时,往往会把问题拆分成几个子问题,对其中的一个或多个问题递归地处理,并在分治之前或之后进行一些预处理、汇总处理。这时候我们可以得到关于这个算法复杂度的一个递推方程,求解此方程便能得到算法的复杂度。其中很常见的一种递推方程就是这样的:

    设常数 a >= 1,b > 1,f(n) 为函数,T(n) 为非负整数,T(n) = a T(n / b) + f(n),则有:

    1. 若 f(n)=O(nlogb⁡a−ε),ε>0,那么 T(n)=Θ(nlogb⁡a)。
    2. 若 f(n)=Θ(nlogb⁡a),那么 T(n)=Θ(nlogb⁡alog⁡n)。
    3. 若 f(n)=Ω(nlogb⁡a+ε),ε>0,并且对于某个常数 c < 1 和充分大的 n 有 af(n/b)≤cf(n),那么 T(n)=Θ(f(n))。

    比如常见的二分查找算法,时间复杂度的递推方程为 T(n) = T(n / 2) + θ(1),显然有 nlogb⁡a=n0=Θ(1),满足 Master 定理第二条,可以得到其时间复杂度为 T(n) = θ(log n)。

    再看一个例子,T(n) = 9 T(n / 3) + n,可知 nlogb⁡a=n2,令ε取 1,显然满足 Master 定理第一条,可以得到 T(n) = θ(n ^ 2)。

    来一个稍微复杂一点儿例子,T(n) = 3 T(n / 4) + n log n。nlogb⁡a=O(n0.793),取ε = 0.2,显然当 c = 3 / 4 时,对于充分大的 n 可以满足 a * f(n / b) = 3 * (n / 4) * log(n / 4) <= (3 / 4) * n * log n = c * f(n),符合 Master 定理第三条,因此求得 T(n) = θ(n log n)。

    运用 Master 定理的时候,有一点一定要特别注意,就是第一条和第三条中的ε必须大于零。如果无法找到大于零的ε,就不能使用这两条规则。

    参考: https://blog.gocalf.com/algorithm-complexity-and-master-theorem.html

  4. 斐波那契数列
    问题描述:Fibonacci 数(Fibonacci Number)的定义是:F(n) = F(n - 1) + F(n - 2),并且 F(0) = 0,F(1) = 1。对于任意指定的整数 n(n ≥ 0),计算 F(n) 的精确值,并分析算法的时间、空间复杂度。

    // 斐波那契数列 递归
    function fibonacci(n){
          
          
        if (n==0) {
          
          
            return 0;
        }
        if (n==1) {
          
          
            return 1;
        }
        return fibonacci(n-1) + fibonacci(n-2);
    }
    

    一个看起来很直观、用起来很恐怖的算法就是递归法。根据 Fibonacci 的递推公式,对于输入的 n,直接递归地调用相同的函数分别求出 F(n - 1) 和 F(n - 2),二者相加就是结果。递归的终止点就是递推方程的初值,即 n 取 0 或 1 的时候。

    这个算法的时间复杂度有着跟 Fibonacci 类似的递推方程:T(n) = T(n - 1) + T(n - 2) + O(1),很容易得到 T(n) = O(1.618 ^ n)(1.618 就是黄金分割,(1+5)/2)。空间复杂度取决于递归的深度,显然是 O(n)

    // 斐波那契数列 递推法
    function fibonacci(n){
          
          
        if (n == 0) {
          
          
            return 0;
        }
        if (n == 1) {
          
          
            return 1;
        }
        let a = [0, 1];
        let n_i = 1;
        while(n_i < n) {
          
          
            n_i++;
            // 交换位置
            let next = a[0] + a[1];
            a[0] = a[1];
            a[1] = next;
        }
        return a[1];
    }
    

    虽然只是一字之差,但递推法的复杂度要小的多。这个方法就是按照递推方程,从 n = 0 和 n = 1 开始,逐个求出所有小于 n 的 Fibonacci 数,最后就可以算出 F(n)。由于每次计算值需要用到前两个 Fibonacci 数,更小的数就可以丢弃了,可以将空间复杂度降到最低。显然时间复杂度是 O(n),空间复杂度是 O(1)

    比较一下递归法和递推法,二者都用了分治的思想——把目标问题拆为若干个小问题,利用小问题的解得到目标问题的解。二者的区别实际上就是普通分治算法和动态规划的区别。

    矩阵法:~

  5. 归并排序

    // 归并排序
    function process(arr, L, R) {
          
          
        if (L === R) {
          
          
            return;
        }
        let mid = L + ((R - L) >> 1);
        process(arr,L,mid);
        process(arr,mid+1,R);
        merge(arr,L,mid,R);
    }
    
    function merge(arr,L,mid,R) {
          
          
        let help = [];
        let i = 0;
        let p1 = L;
        let p2 = mid +1;
        while (p1 <= mid && p2 <=R) {
          
          
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        while(p1 <= M) {
          
          
            help[i++] = arr[p1++]
        }
        while(p2 <= R) {
          
          
            help[i++] = arr[p2++]
        }
        for(let i = 0;i<help.length;i++) {
          
          
            arr[L + i] = help[i]
        }
    }
    

猜你喜欢

转载自blog.csdn.net/sinat_29843547/article/details/128727143
今日推荐