一.算法复杂度

目录

(一)数据结构前言

(1)数据结构

(2)算法

(二)算法效率

(1)来个题

(2)复杂度的概念

(三)时间复杂度

为什么不直接计算程序的运行时间?

T(N)这个函数式到底是什么?

(1)大O的渐进表示法

推导大O阶规则

(2)时间复杂度计算

①Fun1

②Fun2

③Fun3

④Fun4

⑤Fun5

⑥BubbleSort

⑦Fun7

⑧Fac

总结:

(四)空间复杂度

空间复杂度计算

①BubbleSort

②Fac

(五)常见复杂度对比

(六)复杂度算法题

旋转数组

算法1(暴力旋转)

算法2(额外数组法)

算法3(三次翻转法)


(一)数据结构前言

(1)数据结构

数据结构(Data Structure) 是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。

(2)算法

一系列的计算步骤,用来将输入数据转化成输出结果。

(二)算法效率

(1)来个题

直接上代码,这是一个旋转数组题https://leetcode.cn/problems/rotate-array/description/

cpp 复制代码
void rotate(int* nums, int numsSize, int k)
{
    int i = 0;
    while (k--)
    {
        printf("k = %d:\n",k);
        int end = nums[numsSize - 1];
        for (i = numsSize - 1;i > 0;i--)
        {
        nums[i] = nums[i-1];
        }
        nums[0] = end;
        for (i = 0;i < numsSize;i++)
        {
        printf("nums[%d] = %d\n", i, nums[i]);
        }
    }
}

提交代码为什么无法通过?算法效率太低了,详解在第(六)点。

(2) 复杂度的概念

一个算法的好坏,一般从 时间复杂度 和 空间复杂度 来衡量。

  • 时间复杂度主要衡量算法的运行快慢。

  • 空间复杂度主要衡量一个算法运行所需的额外空间。

(三)时间复杂度

在计算机科学中,算法的时间复杂度是一个函数式T(N),它定量描述了算法的运行时间。

为什么不直接计算程序的运行时间?

  1. 程序运行时间与 编译环境 和 运行机器的配置 都有关系。

  2. 同一个算法程序,用一个老低配置机器和新高配置机器,运行时间不同。

  3. 时间只能程序写好后测试,不能写程序前。

T(N)这个函数式到底是什么?

  1. T(N)这个函数式计算了程序的执行次数。

  2. 算法程序被编译后生成二进制指令,程序运行,就是cpu在执行这些编译好的指令。

  3. 假设每句指令执行时间基本一样,那么执行次数和运行时间就是成正比例。

  4. 这样就脱离了具体的编译运行环境。

  5. 执行次数就可以表示时间效率的优劣。

在实际计算中

  • 一般只需要计算程序能代表增长量级的大概执行次数。

  • 复杂度的表示通常使用大O的渐进表示法。

(1)大O的渐进表示法

大O符号(Big O notation)是用于描述函数渐进行为的数学符号。

推导大O阶规则

  1. 时间复杂度函数式T(N)中,只保留最高阶项,去掉那些低阶项。因为当N不断变大的时候,低阶项对结果影响越来越小,当N无穷大的时候,就可以忽略不计了。

  2. 如果最高阶项存在且不是1,则去除这个项的常数系数。因为当N不断变大,这个系数对结果的影响越来越小,当N无穷大的时候,就可以忽略不计了。

  3. T(N)中如果只有常数项,用1代替所有加法常数。

(2)时间复杂度计算

①Fun1

cpp 复制代码
void Fun1(int N) 
{
    int count = 0;
    for (int i = 0;i < N;++i)
    {
        for (int j = 0;j < N;++j) 
        {
            ++count;
        }
    }
    for(int k = 0; k < 2*N;++k)
    {
        ++count;
    }
    int M = 10;
    while(M--)
    {
        ++count;
    }
}

以上算法程序时间复杂度为:O(N^2)

②Fun2

cpp 复制代码
void Fun2(int N)
{
    int count = 0;
    for (int k = 0; k < 2 * N; ++k)
    {
        ++count;
    } 
    int M = 10;
    while (M--)
    {
        ++count;
    } 
    printf("%d\n", count);
}

以上算法程序时间复杂度为:O(N)

③Fun3

cpp 复制代码
void Func3(int N, int M)
{ 
    int count = 0; 
    for (int k = 0; k < M; ++ k) 
    { 
        ++count; 
    } 
    for (int k = 0; k < N ; ++ k) 
    { 
        ++count; 
    } 
    printf("%d\n", count); 
}

以上算法程序时间复杂度为:O(N)

④Fun4

cpp 复制代码
void Func4(int N)
{ 
    int count = 0; 
    for (int k = 0; k < 100; ++ k) 
    { 
        ++count; 
    } 
    printf("%d\n", count); 
}

以上算法程序时间复杂度为:O(1)

⑤Fun5

cpp 复制代码
const char* strchr( const char* str, int character) 
{ 
    const char* p_begin = str; 
    while (*p_begin != character) 
    { 
        if (*p_begin == '\0') 
        {  
            return NULL;   
        }        
        p_begin++; 
    } 
    return p_begin; 
}

以上算法程序时间复杂度为:O(N)

⑥BubbleSort

cpp 复制代码
void BubbleSort(int* a, int n)
{
    assert(a);
    for (size_t end = n; end > 0; --end)
    {
        int exchange = 0;
        for (size_t i = 1; i < end; ++i)
        {
            if (a[i - 1] > a[i])
            {
                Swap(&a[i - 1], &a[i]);
                exchange = 1;
            }
        } 
        if (exchange == 0) 
        {
            break;
        }
    }
}

以上算法程序时间复杂度为:O(N^2)

⑦Fun7

cpp 复制代码
void Fun7(int n)
{
    int cnt = 1;
    while (cnt < n)
    {
        cnt *= 2;
    }
}

以上算法程序时间复杂度为:O(logN)

⑧Fac

cpp 复制代码
long long Fac(size_t N)
{
    if (0 == N) 
    {
        return 1;
    }
    return Fac(N - 1) * N;
}

以上算法程序时间复杂度为:O(N)

总结

有些算法的时间复杂度存在最好、平均和最坏情况。

  • 最坏情况(上界):任意输入规模的最大运行次数。

  • 平均情况:任意输入规模的最大运行次数。

  • 最好情况(下界):任意输入规模的最小运行次数。

(四)空间复杂度

空间复杂度 是对一个算法在运行过程中因为算法的需要 额外开辟的空间。

  • 空间复杂度计算的是变量的个数。

  • 空间复杂度使用大O渐进表示法。

函数运行所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间就已经确定好了,因此空间复杂度主要是计算函数在运行时候显式申请的(程序员主动写代码申请的)额外空间

空间复杂度计算

①BubbleSort

cpp 复制代码
void BubbleSort(int* a, int n)
{
    assert(a);
    for (size_t end = n; end > 0; --end)
    {
        int exchange = 0;
        for (size_t i = 1; i < end; ++i)
        {
            if (a[i - 1] > a[i])
            {
                Swap(&a[i - 1], &a[i]);
                exchange = 1;
            }
        } 
        if (exchange == 0) 
        {
            break;
        }
    }
}

以上算法程序空间复杂度为:O(1)

②Fac

cpp 复制代码
long long Fac(size_t N)
{
    if (0 == N) 
    {
        return 1;
    }
    return Fac(N - 1) * N;
}

以上算法程序空间复杂度为:O(N)

(五)常见复杂度对比

|-------|-------------|----------------|----------|-------------|-------------|-------------|
| n | log2^n | n*log2^n | n^2 | n^3 | 2^n | n! |
| 4 | 2 | 8 | 16 | 64 | 16 | 24 |
| 8 | 3 | 24 | 64 | 512 | 256 | 80320 |
| 10 | 3.32 | 33.2 | 100 | 1000 | 1024 | 3628800 |
| 16 | 4 | 64 | 256 | 4096 | 65536 | 2.1*10^13 |
| 32 | 5 | 160 | 1024 | 32768 | 4.3*10^9 | 2.6*10^35 |
| 128 | 7 | 896 | 16384 | 2097152 | 3.4*10^38 | ∞ |
| 1024 | 10 | 10240 | 1048576 | 1.07*10^9 | ∞ | ∞ |

(六)复杂度算法题

旋转数组

算法1(暴力旋转)

思路:旋转k次,一次一次旋转数组。

cpp 复制代码
void rotate1(int* nums, int numsSize, int k)
{
    int i = 0;
    while (k--)
    {
        printf("k = %d:\n",k);
        int end = nums[numsSize - 1];
        for (i = numsSize - 1;i > 0;i--)
        {
            nums[i] = nums[i-1];
        }
        nums[0] = end;
    }
}

以上算法时间复杂度为O(N^2),空间复杂度为O(1)。

算法2(额外数组法)

  1. 创建新数组。

  2. 在新数组中实现旋转。

  3. 再拷贝到旧数组。

cpp 复制代码
void rotate2(int* nums, int numsSize, int k) 
{
    int* tmpArr = malloc(numsSize*sizeof(int));
    int i = 0;
    for (i = 0;i < numsSize;++i)
    {
        tmpArr[i] = nums[(i + k) % numsSize];
    }
    for (i = 0;i < numsSize;++i) 
    {
        nums[i] = tmpArr[i];
    }
}

以上算法时间复杂度为O(N),空间复杂度为O(N)。

算法3(三次翻转法)

  1. 旋转前 numsSize - k 个元素。

  2. 再从下标numsSize - k旋转k个元素。

  3. 最后旋转整个数组。

reverse函数:

cpp 复制代码
void reverse(int* nums,int begin,int end)
{
    int tmp = 0;
    while (begin < end) 
    {
        tmp = nums[begin];
        nums[begin] = nums[end];
        nums[end] = tmp;
        ++begin;
        --end;
    }
}

rotate3函数:

cpp 复制代码
void rotate3(int* nums, int numsSize, int k) 
{
    k = k % numsSize;
    test014_4_rotate3_reverse(nums,0,numsSize - k - 1);
    test014_4_rotate3_reverse(nums,numsSize - k,numsSize - 1);
    test014_4_rotate3_reverse(nums,0,numsSize - 1);
}

以上算法时间复杂度为O(N),空间复杂度为O(1)。

相关推荐
wen__xvn1 小时前
码蹄杯刷题
数据结构·c++·算法
少控科技1 小时前
QT进阶日记009
开发语言·qt
人工智能培训1 小时前
如何持续、安全地向大模型注入新知识?
人工智能·python·算法·大模型·大模型学习·大模型应用工程师·大模型工程师证书
CodeCraft Studio2 小时前
从框架到体验:Qt + Qtitan 构建制造业嵌入式UI整体解决方案
开发语言·qt·ui·gui·嵌入式开发·hmi·制造业嵌入式ui
AIFQuant2 小时前
如何快速接入贵金属期货实时行情 API:python 实战分享
开发语言·python·金融·数据分析·restful
Remember_9932 小时前
【数据结构】Java对象比较全解析:从equals到Comparable与Comparator,再到PriorityQueue应用
java·开发语言·数据结构·算法·leetcode·哈希算法
郝学胜-神的一滴2 小时前
深入浅出网络协议:从OSI七层到TCP/IP五层模型全解析
开发语言·网络·c++·网络协议·tcp/ip·程序人生
夏乌_Wx2 小时前
练题100天——DAY39:单链表练习题×5
c语言·数据结构·算法·链表
qq_406176142 小时前
吃透JS异步编程:从回调地狱到Promise/Async-Await全解析
服务器·开发语言·前端·javascript·php