时间复杂度与空间复杂度(探索篇)

时间复杂度的种类(按照复杂度的增长趋势来进行排列)

  • O(1)
  • O(logn)
  • O(n)
  • O(nlogn)
  • O(n2)
  • O(n3)
  • O(2n)
  • O(n!)

时间复杂度增长图

时间复杂度为O(1)的操作

  • if语句中的判断 例如:if(i === 1)。
  • 赋值/加减乘除运算。例如:a=1,result = 3+4,result = n*2,result = 10000 * 10000。
  • 数组的push与pop操作。例如:array.push(‘a’),array.pop()。
  • map操作。map.set(1,1),map.get(1,1)。
  • 在计算的时候O(1)一般会被忽略,因为时间复杂度是取层级最高的那个。

时间复杂度为O(n)的操作

  • for循环,while循环(不使用二分搜索)
  • 例如:for(let i=0;i<n;i++); let i=0;while(i<n)。
    例题:
    let a = 0
    // O(n)操作
    for(let i=0;i<n;i++){
        // O(1)操作
        a+=i; 
    }

时间复杂度为O(n2)的操作

  • 嵌套for循环,嵌套while循环
    例如:
    // O(n)的平方
    for(let i=0;i<n;i++){
        for(let j=0;j<n;j++){
            // O(1)操作
            a+=j
        }
    }

会被误以为是O(n^2)但实际是O(n)的例子1:

    // O(n)的操作
    for(let i=0;i<n;i++){
        ...
    }
    // O(n)的操作
    for(let j=0;j<n;j++){
        ... 
    }

会被误以为是O(n^2)但实际是O(n)的例子2:

    // O(n)的操作
    while(i<n){
        i++,j=1;
        // 当符合这一层的条件时,则进入这一层循环,使得i++
        while(j<n){
            // 在这里i++,j++,当不满足这一while循环的条件时则跳出循环。
            // 但是因为在此操作中进行了i++,所以外层也是会从已经加过的i开始
            // 例如进入到while循环时的i是4,出去的时候是8,则外层循环仍然是从8开始遍历
            // 符合O(n)的策略
            i++;
            j++;
        }
    }

时间复杂度为logn的操作

  • 二分查找
    let left = 0,
    right = arr.length - 1
    while (left <= right) {
        let middle = Math.floor((left + right) / 2)
        if (arr[middle] > target) {
            right = middle - 1
        } else if (arr[middle] < target) {
            left = middle + 1
        } else if (arr[middle] === target) {
            return middle
        }
    }
    return -1

时间复杂度为nlogn的操作

  • 编程语言中arr.sort

时间复杂度为n3

  • 对标leetcode四数之和

时间复杂度为2n

  • 对于2n在树结构和递归结构中比较常见,比如说n步,每步则有两个选择则变成2 * 2 * 2 * 2…,则会有2n的时间复杂度

可优化的结构(从低一级的复杂度寻找灵感)

  • 如果此时的的时间复杂度为O(n)则可优化的程度为O(logn),比如说查找已排序的数组中的元素,我们可以从for循环->二分查找去降低复杂度。
  • 如果此时的的时间复杂度为O(nlogn)则可优化的程度为O(n),比如说我们需要将其排序,去解除此题,我们可以考虑使用map,set去将时间复杂度降低为O(n)
  • 如果此时的的时间复杂度为O(n2)则可优化的程度为O(nlogn),比如说我们遇到嵌套循环,这个时候我们可以考虑将其排序在使用一个for循环去降低时间复杂度。

时间复杂度的计算

对于复杂度的计算,我们是取复杂度最高的一项作为总体复杂度,前面的常数忽略。

  1. n3+n2+1的时间复杂度为n3
  2. 2n2+3n+6的时间复杂度为n^2

空间复杂度

空间复杂度为O(1)的例子

创建了一个为a的变量,其值为1

  • const a = 1
  • int a = 2
    对应反转链表题目:
    var reverseList = function(head) {
        // 我们在这个过程中只是创建了几个指针,对于所接收到的链表进行反转
        // 而在这个过程中我们并没有自己去创建一个长度为n的链表,所以空间复杂度为O(1)
        let prev = null
        let curr = head
        while(curr){
            const next = curr.next
            curr.next = prev
            // 之前的节点为当前的节点
            prev = curr
            // 当前的节点为下一个节点
            curr = next
        }
        // 此时prev就是为头结点
        return prev;
    };

空间复杂度为O(n)的例子

  • 定义一个长度为n的数组
  • 定义一个长度为n的set和map
  • 用for循环生成一个长度为n的链表
    对应两数相加题目:
    // 在计算过程中会生成一个长度为n的链表并进行返回,所以空间复杂度为O(n)
    var addTwoNumbers = function(l1, l2) {
        let listnode = new ListNode()
        let curr = listnode
        let carry = 0
        while(l1 !== null || l2 !== null){
            let sum = 0
            if(l1 !== null){
                sum += l1.val
                l1 = l1.next
            }
            if(l2 !== null){
                sum += l2.val
                l2 = l2.next
            }
            sum += carry
            // 在这个地方每次去生成一个新的节点,并向下续加
            curr.next = new ListNode(sum%10)
            carry = Math.floor(sum/10) 
            curr = curr.next
        }
        if(carry > 0){
            curr.next = new ListNode(carry)
        }
        return listnode.next
    };

空间复杂度为O(n2)的例子

  • 二维数组
  • 一个一维数组存放一个长度为n的set或者map或者链表

时间复杂度与空间复杂度的取舍

分析题目的时间复杂度与空间复杂度:

    function twoSum(nums, target) {
        // 定义一个变量,空间复杂度为O(1)
        let n = num.length
        // 因为为双层for循环,时间复杂度为为O(n^2^)
        for (let i = 0; i < n; i++) {
            for (let j = i + 1; j < n; j++) {
                if (nums[i] + nums[j] === target) {
                    // 返回一个长度为2的数组,则空间复杂度为O(1)
                    return [nums[i], nums[j]]
                }
            }
        }
        return []
    }

 所以此题目的时间复杂度为O(n2),空间复杂度为O(1)。

    var twoSum = function (nums, target) {
        // 创建一个变量,空间复杂度为O(1)
        let len = nums.length
        // 我们创建了map,并且会将nums中的元素放入到这个map中,所以此时空间复杂度为O(n)
        let map = new Map()
        // 一个for循环所以此时的时间复杂度为O(n)
        for (let i = 0; i < len; i++) {
            let currNum = target - nums[i]
            // map.has操作时间复杂度为O(1)
            if (map.has(currNum)) {
                // map.get操作时间复杂度为O(1)
                // 返回一个长度固定为2的数组,所以此时的空间复杂度也为O(1)
                return [map.get(currNum), i]
            } else {
                map.set(nums[i], i)
            }
        }
    };

 所以此题目的时间复杂度为O(n),空间复杂度为O(n)。
对于常见算法来讲,我们需要尽量取舍时间复杂度比较低的那个,除非题目显式的要求(原地算法),如果时间复杂度一样,则看空间复杂度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值