JavaScript-算法(一)算法复杂度

JavaScript-算法(一)算法复杂度

一、算法复杂度

给定一个或多个已知值,通过一系列的运算输出所需结果的过程 叫 算法 一个需求可能可以通过多种算法来实现 ,学习算法让我们了解怎样的算法效率更高更快,从而让我们针对某个需求做出最合适的选择。

如果你想在编程的道路上走得更远一些,或涉猎到更多的数据逻辑,算法对你的帮助很大~

讨论一个算法的好坏,最主要的两个方面:该算法所需时间 - 时间复杂度 ,该算法占用空间 - 空间复杂度。 耗时短占用低肯定是最优选择,而往往我们需要在两者中找到一个平衡点,作为一名程序开发者,大多数情况下,时间复杂度比空间复杂度的影响会更大一些,能做的改良和优化也更多,所以一般如果不做特别说明,我们讨论算法复杂度的时候都是指时间复杂度

1.时间复杂度

一个算法所需要的时间需要具体上机运行才知道,很显然不可能每个都验证一遍,再做选择,我们可以对算法好事进行合理的预测。

一个算法中语句的执行次数 我们叫做时间频度,记作T(n),n 表示问题规模,n增大T(n) 也随之增大,二我们预估算法的效率,就是对T(n)的变化趋势进行分析,为了方便分析,我们需要简化一下T(n)函数,所以我们引入时间复杂度的概念:若有某个辅助函数f(n),使得当n趋近于无穷大时, T(n)/f(n) 的极限值不等于0的常数,则称f(n) 是 T(n) 的同数量级函数,记作 T(n) = O(f(n)),O(f(n)) 就是时间复杂度的表示法。

总结一下 : 用 O(f(n)) 表示 算法的时间复杂度

时间复杂度 是描述一个算法 随着问题规模不断增大之后的所需要消耗的时间的一个变化趋势

2.复杂度计算 举例

理论上 ,我们对 O(f(n)) 中的 f(n) 没有写法限制 ,但是因为我们关注的是 随 n 增大复杂度的变化趋势,所以各常数项和系数我们是直接省略不考虑的。

简单的例子:

// O(1)
let a = 10
let b = 20
console.log(a+b)
//T(n) = 1 + 1 + 1 = 3  时间频度   (语句执行几次)
//O(1)                  时间复杂度 (常数项和系数省略)

// O(n)
function fn(n){
        let sum = 0
        for (let i = 1; i < n ; i++) {
            sum += i
        }
        return sum
    }
// T(n) = 1 + n + 1 = n +2
// O(n)                     // 只看最高项 不看系数

// O(n^2)
 function fn(n){
        let a = 0;
        for (let i = 1; i < n ; i++) {
            for (let j = 1;j<n;j++){
                a = i*j
            }
        }
        re function fn(n){
        let a = 0;
        for (let i = 1; i < n ; i++) {
            for (let j = 1;j<n;j++){
                a = i*j
            }
        }
        return a
    }
// T(n) = 1 + n + n*n + n*n + 1 = 2*n^2 +n +2
// O(n^2)

// O(n^2)
 function fn(n){
        let a = 0
        for (let i = 0; i <= n; i++) {
            for (let j=i;j<=n;j++){
                a = i*j
            }
        }
        return a
    }
// T(n) = 1 + n + n*(1+n)/2 + 1 = 1/2*n^2 + 2/3*n +2 
// O(n^2) 
     
// O(n^2)
function fn(n, m) {
        let a = 0
        for (let i = 0; i < n; i++) {
            for (let j = 1; j < m; j++) {
                a = i * j
            }
        }
        return a
    }

// T(n) = 1 + n + n*m +n*m +1 = 2*n*m + n + 2
// O(n^2)  m n 只是一种描述 在计算时间复杂度的时候同样会把它当作n
     
 function fn(n){
        let a = 0
        for (let i = 0; i < n; i++) {
            for (let j = 0; j < n; j++) {
                for (let k = 0; k < n; k++) {
                    a = i* j * k
                }
            }
        }
    }
// O(n³)

function fn(n){
        let a = 0
        for (let i = 1 ; i < n; i*=2) {
            a+=i
        }
        return a
    }
// O(log₂n)
     
 function fn(n){
        let a  =0
        for (let i = 1; i <=n ; i++) {
            for (let j = 1; j <=n ; j*=2) {
                a += i*j
            }
        }
        return a 
    }
// O(n*log₂n)
斐波那契数列

举一个明显的例子:斐波那契数列: 0 1 1 2 3 5 8 … 如何计算斐波那契数列呢?

这是最简单的实现 但是有一个问题 ,他的时间复杂度及其的恐怖,达到了2^n 。

function Fibonacci(n) {
    if (n === 1) return 0
    if (n === 2) return 1
    // 等于前两个数组的和
    return Fibonacci(n - 1) + Fibonacci(n - 2)
}
//O(2^n) 时间复杂度是及其庞大的
console.time('耗时') //耗时: 1139.516845703125 ms
Fibonacci(40)
console.timeEnd('耗时')

对其进一步优化

function Fibonacci(n) {
    if (n === 1) return 0
    if (n === 2) return 1
    let n1 = 0, n2 = 1, cur;
    // 保存 n-1 n-2 项
    for (let i = 3; i <= n; i++) {
        cur = n1 + n2
        n1 = n2
        n2 = cur
    }
    return cur
}
// O(n)
console.time('耗时') //耗时: 0.0478515625 ms
Fibonacci(40)
console.timeEnd('耗时')

上述的方式,将其n-1,n-2 项进行了存储 ,不用每次都重新开始计算,有效的节省了开销。可见,算法的魅力还是很大的~

百钱百鸡 问题
   百钱百鸡 问题
   描述: 100文钱  要买 100 只鸡
       小鸡 -- 3只/1文
       母鸡 -- 1只/3文
       攻击 -- 1只/5文
       每种鸡都要有 ,总共刚好100只,刚好花完100文钱,
       一共有几种买法

原始方法进行求解:

let x, y, z;// x小鸡 y母鸡 z公鸡 
for (let x = 1; x <= 98; x++) {
    for (let y = 1; y <= 98; y++) {
        for (let z = 1; z <= 98; z++) {
            if (x / 3 + y * 3 + z * 5 === 100 && x + y + z === 100) {
                console.log(`小鸡${x}只,母鸡${y}只,公鸡${z}`)
                /*
                *   小鸡78只,母鸡18只,公鸡4只
                *   小鸡81只,母鸡11只,公鸡8只
                *   小鸡84只,母鸡4只,公鸡12只
                * */
            }
        }
    }
}
// O(n^3)
// 时间复杂度比较高 此时 需要进行改进

如果此时结合我们进一步的分析和思考,可以寻找 x y z,三者之间的共同关系 ,分析思路:

/*
*   x + y + z = 100  ①
*   x/3 + 3y +5z = 100 ②
*
*   ② *  3 =>
*       x + 9y + 15z = 300 ③
*   ③ - ① =>
*       8y + 14z = 200
*       化简得:
*       y = 25 + 7z/4
*   设 n = z/4
*   得 y = 25-7n
*      x = 75 + 3n
*      z = 4n
*
*   因为 z = 4n  不能大于 100 n<25
*   因为 y = 25 - 7n 不能小于0  n<4
*   因为 x = 75 + 3n 不能大于100 n<8
* */

经过上述 分析与计算 ,对代码进行如下优化:

for (let i = 1; i < 4; i++) {
    let x = 75 + 3*i;
    let y = 25-7*i;
    let z = 4*i;
    if (x / 3 + y * 3 + z * 5 === 100 && x + y + z === 100) {
        console.log(`小鸡${x}只,母鸡${y}只,公鸡${z}`)
        /*
        *   小鸡78只,母鸡18只,公鸡4只
        *   小鸡81只,母鸡11只,公鸡8只
        *   小鸡84只,母鸡4只,公鸡12只
        * */
    }
}
// O(n) 时间复杂度

可见 具有算法思维是多么的重要!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

貂蝉的腿毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值