【代码随想录】算法学习准备知识

本文深入探讨算法性能,分析了时间复杂度中的O(n)、O(n^2)和O(nlogn)的界限,指出当n增大时,需要考虑更优的解法,如从O(n)转向O(logn)。同时,讲解了递归算法的时间复杂度,举例说明了空间复杂度如何从O(1)到O(n)的变化,并提到了二分查找的空间复杂度为O(logn)。文章强调了在实际编程中,理解和估算算法的时间和空间复杂度对于避免超时和内存限制的重要性。
摘要由CSDN通过智能技术生成

算法性能分析

代码风格

在这里插入图片描述
在这里插入图片描述

时间复杂度分析

logn的底数是多少?
在这里插入图片描述

算法超时评测

java的输入形式
在这里插入图片描述
在leetcode上练习算法的时候应该都遇到过一种错误是“超时”。

也就是说程序运行的时间超过了规定的时间,一般OJ(online judge)的超时时间就是1s,也就是用例数据输入后最多要1s内得到结果,暂时还不清楚leetcode的判题规则,下文为了方便讲解,暂定超时时间就是1s。

如果写出了一个 O ( n ) O(n) O(n)的算法 ,其实可以估算出来n是多大的时候算法的执行时间就会超过1s了。

如果n的规模已经足够让 O ( n ) O(n) O(n)的算法运行时间超过了1s,就应该考虑log(n)的解法了。

import java.util.*;

public class Main {
    // o(n)
    public static void function1(long n) {
        System.out.println("o(n)算法:");
        long k = 0;
        for (long i = 0; i < n; i++) {
            k++;
        }
    }

    // o(n^2)
    public static void function2(long n) {
        System.out.println("o(n^2)算法:");
        long k = 0;
        for (long i = 0; i < n; i++) {
            for (long j = 0; j < n; j++) {
                k++;
            }
        }
    }

    // o(nlogn)
    public static void function3(long n) {
        System.out.println("o(nlogn)算法:");
        long k = 0;
        for (long i = 0; i < n; i++) {
            for (long j = 1; j < n; j = j * 2) {
                k++;
            }
        }
    }

    public static void main(String[] args) {
        while (true) {
            Scanner scan = new Scanner(System.in);
            System.out.println("输入n:");
            int n = scan.nextInt();
            long startTime = System.currentTimeMillis();
            function1(n);
            // function2(n);
            // function3(n);
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            System.out.println("算法耗时==" + costTime + "ms");
        }
    }
}

递归算法复杂度

递归算法的时间复杂度本质上是要看: 递归的次数 * 每次递归中的操作次数。

那再来看代码,这里递归了几次呢?

每次n-1,递归了n次时间复杂度是O(n),每次进行了一个乘法操作,乘法操作的时间复杂度一个常数项O(1),所以这份代码的时间复杂度是 n × 1 = O(n)。

//o(n)
int function2(int x, int n) {
    if (n == 0) {
        return 1; // return 1 同样是因为0次方是等于1的
    }
    return function2(x, n - 1) * x;
}
//o(logn)
int function4(int x, int n) {
    if (n == 0) {
        return 1;
    }
    int t = function4(x, n / 2);// 这里相对于function3,是把这个递归操作抽取出来
    if (n % 2 == 1) {
        return t * t * x;
    }
    return t * t;
}

空间复杂度

空间复杂度是考虑程序(可执行文件)的大小么?
很多同学都会混淆程序运行时内存大小和程序本身的大小。这里强调一下空间复杂度是考虑程序运行时占用内存的大小,而不是可执行文件的大小。

空间复杂度是准确算出程序运行时所占用的内存么?
不要以为空间复杂度就已经精准的掌握了程序的内存使用大小,很多因素会影响程序真正内存使用大小,例如编译器的内存对齐,编程语言容器的底层实现等等这些都会影响到程序内存的开销。

所以空间复杂度是预先大体评估程序内存使用的大小。

说到空间复杂度,我想同学们在OJ(online judge)上应该遇到过这种错误,就是超出内存限制,一般OJ对程序运行时的所消耗的内存都有一个限制。

为了避免内存超出限制,这也需要我们对算法占用多大的内存有一个大体的预估。

同样在工程实践中,计算机的内存空间也不是无限的,需要工程师对软件运行时所使用的内存有一个大体评估,这都需要用到算法空间复杂度的分析。

来看一下例子,什么时候的空间复杂度是 O ( 1 ) O(1) O(1)呢,代码如下:

int j = 0;
for (int i = 0; i < n; i++) {
    j++;
}

第一段代码可以看出,随着n的变化,所需开辟的内存空间并不会随着n的变化而变化。即此算法空间复杂度为一个常量,所以表示为大O(1)。

什么时候的空间复杂度是O(n)?

当消耗空间和输入参数n保持线性增长,这样的空间复杂度为O(n),来看一下这段代码

int* a = new int(n);
for (int i = 0; i < n; i++) {
    a[i] = i;
}

我们定义了一个数组出来,这个数组占用的大小为n,虽然有一个for循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,随着n的增大,开辟的内存大小呈线性增长,即 O(n)。
空间复杂度是logn的情况确实有些特殊,其实是在递归的时候,会出现空间复杂度为logn的情况。

int binary_search( int arr[], int l, int r, int x) {
    if (r >= l) {
        int mid = l + (r - l) / 2;
        if (arr[mid] == x)
            return mid;
        if (arr[mid] > x)
            return binary_search(arr, l, mid - 1, x);
        return binary_search(arr, mid + 1, r, x);
    }
    return -1;
}

都知道二分查找的时间复杂度是O(logn),那么递归二分查找的空间复杂度是多少呢?
递归算法的空间复杂度 = 每次递归的空间复杂度 * 递归深度
我们依然看 每次递归的空间复杂度和递归的深度
每次递归的空间复杂度可以看出主要就是参数里传入的这个arr数组,但需要注意的是在C/C++中函数传递数组参数,不是整个数组拷贝一份传入函数而是传入的数组首元素地址。
也就是说每一层递归都是公用一块数组地址空间的,所以 每次递归的空间复杂度是常数即:O(1)。
再来看递归的深度,二分查找的递归深度是logn ,递归深度就是调用栈的长度,那么这段代码的空间复杂度为 1 * logn = O(logn)。
大家要注意自己所用的语言在传递函数参数的时,是拷贝整个数值还是拷贝地址,如果是拷贝整个数值那么该二分法的空间复杂度就是O(nlogn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值