时间复杂度与空间复杂度

1.时间复杂度

1.为什么要引入时间复杂度?

因为在运行程序时,环境不同(电脑配置,版本等不同),则具体的运行时间就不同,为了排除硬件条件的影响,我们引入时间复杂度,时间复杂度计算的是某个程序基础操作的执行次数,这也很容易理解,基础操作执行的次数越多,程序运行的自然就慢,通过计算时间复杂度我们便可知晓这个程序的运行效率

 int M=100,N=60;
    int count=0;
    public void forsth(){
        for(int i=0;i<N;i++){
           
            for(int j=0;j<M;j++){
                count++;
       //基础操作为count++
       //那么时间复杂度就是算count++执行了多少次
       //时间复杂度为O(M*N)
            }
        }
    }
2.时间复杂度的表示

时间复杂度只是用来估算一个程序的用时

因此,当我们写出一个程序基础操作的次数的表达式时,我们只取影响最大的项(最高项),且系数取1(即若求出基础操作需要执行为2N次,那么就表示为O(N))

 int M=100,N=60;
    int count=0;
    public void forsth(){
        for(int i=0;i<N;i++){
               count++;
            for(int j=0;j<M;j++){
                count++;
          //这个程序中,基础操作count++就执行了M*N+N次
            }
        }
    }

执行了M*N+N次,显然M*N是一个二次项,N是一个一次项,故舍去N

时间复杂度,表示为O(M*N)

另外,时间复杂度是一个悲观的预期

 public void findFive(int [] nums){
        for(int i=0;i<nums.length;i++){
            if(nums[i]==5){
                System.out.println(i);
                return;
            }

        }
    }
//如果这个数组是int[] nums={4,2,6,4,3,2,5};
//那么就需要找7次
//而如果数组是int[] nums={5,4,2,6,4,3,2};
//那么就只需要找一次
//我们规定在不确定需要多少次基础操作时,取最大值,即最差的情况
//如果这个数组里有N个数,以这样的方法找数组中的5,它的时间复杂度为O(N)
3.通过计算时间复杂度感受一下二分查找的牛逼之处 
在有序数组中查找一个数的朴素方法
 public void findNum(int [] nums,int num){
        for(int i=0;i<nums.length;i++){
            if(nums[i]==num){
                System.out.println(i);
            }
        }
       //时间复杂度为O(N)
       //从头到尾找遍整个数组,判断是不是要找的数字

    }
二分查找

二分查找思想:

每次排除剩余部分的一半,直到最后只剩满足条件的那一个

ex:小张从图书馆借了一堆书,在出图书馆的时候报警器响了,小张打算一本一本试试到底是哪本书造成了报警,但是保安轻蔑一笑,先是把那一堆书分成了两堆,让他们分别通过报警器,再把让报警器响的那堆书再分成两堆,再分别通过警报.....直到找到那本让警报器报警的书

二分查找代码示例:

    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;

        while (left <= right) {
            int mid = (left + right) / 2;

            // 如果找到目标值,则返回索引
            if (arr[mid] == target) {
                return mid;
            }

            // 如果目标值比中间值小,说明在左半部分
            else if (arr[mid] > target) {
                right = mid - 1;
            }

            // 否则目标值比中间值大,说明在右半部分
            else {
                left = mid + 1;
            }
        }
        
        // 目标值不在数组中,返回 -1
        return -1;
    }

  
二分查找的时间复杂度如何计算?

二分查找的基本操作:找到中间数,与所找数进行比较(把书分堆)

有N本书,被分了x次后,成了一本书----N/(2^x)=1,则x=log2 N,时间复杂度为O(log N)

这样的时间复杂度是个什么概念?

如果需要在1000个数中找一个数,普通方法可能要找1000次

但是二分查找法只需要10次

更夸张的是,当要在1000000个数中找一个数,二分法只要20次,普通方法要1000000次

在1000000000个数中找一个数,二分法只要30次,普通方法要1000000000次

但是二分法的使用很受限制

二分查找的使用条件:有序,只查找一个

所以面对无序的数组,它需要先排序,排序的代价是很大的,下面我们顺便看一下冒泡排序和快速排序的时间复杂度

冒泡排序

冒泡排序的思想:

冒泡法的基本操作:相邻数大小的比较

如果一共有n个数,那么冒泡法的时间复杂度为n-1,n-2,n-3,n-4......1的和,即n*(n-1)/2,O(N^2)

快速排序

快速排序的思想:设基准,分左右

通过与基准的比较,基准数的位置是可以完全确定的,同时又可以让原本完全无序的数列稍有顺序

因此,会比通过相邻数对比,确定一个数的位置的冒泡排序法稍有改进

2.空间复杂度

相比时间复杂度,对空间复杂度人们往往没有那么追求低空间复杂度

根据摩尔定律,每隔18个月,硬件性能就增加一倍,所以现在的内存条件较高,因此为了时间复杂度而牺牲一点空间复杂度倒也是没什么的

空间复杂度计算的是程序运行时所需要的额外空间,计算的是额外的变量的个数

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
//最初定义的空间是存放一个int数的空间,即存放参数N
{
 if(N == 0)
 return 1;
 
 return Fac(N-1)*N;
//方法的调用需要创建一个栈帧
//递归算法
}

 因此它的空间复杂度为O(N)

def fibonacci(n):
    if n <= 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

那么你能自己算算上面这个求斐波那契数列的程序的空间复杂度吗?

你是不是觉得应该是O(2^N)?其实不然哈哈哈哈 

空间是可以重复利用,不累计的时间是一去不复返的,累计的

也就是说,Fac(N-1)开辟了空间之后,在再次调用Fac(N-1)时,就用的还是这一片空间

所以空间复杂度其实是O(N)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值