算法开篇==时间复杂度和空间复杂度

news2024/12/23 0:46:42

一、算法的时间复杂度

1.1 定义

     衡量算法执行时间随着输入数据量增加而增加的速度。它通常用大O符号(O)表示,形式如O(n), O(n^{2}), O(\log n)等,其中n代表输入数据的规模。

1.2 渐进分析

时间复杂度关注的是当输入数据量趋向于无穷大时,算法执行时间的增长趋势。因此,它忽略了常数因子和低阶项,专注于最高阶项,通过高数的极限思想可知,当n趋向于无穷大时,常熟因子和低阶项可忽略不计。

1.3 表示方法

O(f(n))表示算法执行的基本操作次数上限为(f(n))乘以某个常数。例如,如果一个算法的时间复杂度是O(n^{2}),意味着算法的操作次数大致上不会超过(Cn^{2})次,其中(C)是一个正的常数。

1.4 常见的时间复杂度表达式类别

O(1):常数时间复杂度,表示算法的执行时间不随输入数据规模改变。

O(\log n):对数时间复杂度,常见于二分查找等高效算法。

O(n):线性时间复杂度,每个元素被处理一次。

O(n\log n):常见于高效的排序算法,如快速排序、合并排序。

O(n^{2}):平方时间复杂度,例如冒泡排序、选择排序。 

O(n^{3}), O(2^{n}),O(n!):分别代表立方时间复杂度、指数时间复杂度和阶乘时间复杂度,这些通常表示算法效率较低。

当n趋向于无穷大时,这些时间复杂度函数的增长速度按照以下顺序排列:

1.O(1):
常数时间复杂度,不会随着n的增加而改变,时间复杂度最低
2.O(\log n):
对数函数增长非常缓慢,是所有这些函数中增长最慢的。
3.O(n):
线性时间复杂度比对数函数增长快,但仍然远远低于其他更高阶的多项式和指数函数。
4.O(n\log n):
这个时间复杂度介于线性和平方之间,通常在算法设计中是一个很好的折衷,例如在排序算法中。
5.O(n^{2}):
平方级别的复杂度比线性时间复杂度增长快得多,但仍然属于多项式级别。
6.O(n^{3}):
高阶多项式,但仍然比指数函数慢。
7.O(2^{n}):
指数函数开始占据主导地位,增长速度远超过多项式。即使是低阶指数函数,如2^{n},也比高阶多项式更快增长。
8.O(n!):
阶乘函数的增长速度是最快的,尤其是当n变得很大时,n!的值远远超过任何n的幂。阶乘函数的增长速度远超指数函数,是所有列出的函数中增长最快的。
总结来说,随着n趋向于无穷大,这些函数的增长速度从慢到快依次是:

O(1)<<O(\log n) < O(n) < O(n\log n) < O(n^{2}) < O(n^{3}) < O(2^{n}) << O(n!)。

在算法设计中,通常优先选择低阶时间复杂度的算法,以确保在处理大规模数据时的效率。

时间复杂度的计算主要是通过分析算法中基本操作的执行次数与输入数据规模(n)之间的关系来完成的。下面通过几组java代码示例来分别展示以上常见的时间复杂度表达式,加深理解。

O(1) 时间复杂度代码示例 

public class ConstantTimeExample {
    public static void main(String[] args) {
        // 初始化一个数组
        int[] numbers = {10, 20, 30, 40, 50};
        
        // 访问数组中的第三个元素(索引为2)
        int thirdElement = numbers[2];
        
        // 打印该元素
        System.out.println("The third element is: " + thirdElement);
    }
}

分析:以上代码在一个给定数组中访问固定索引位置的元素,无论数组有多大,根据数组的数据结构特性知道,可以通过确定的索引来立即访问到任意索引位置的元素,时间复杂度均为O(1),因为只需要操作一次。
 

O(\log n) 时间复杂度代码示例 

这里以经典的二分查找算法为例

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

        while (left <= right) {
            int mid = left + (right - left) / 2; // 防止(left + right)溢出
            if (arr[mid] == target) {
                return mid; // 找到目标值,返回其索引
            } else if (arr[mid] < target) {
                left = mid + 1; // 调整左边界
            } else {
                right = mid - 1; // 调整右边界
            }
        }

        return -1; // 没有找到目标值,返回-1
    }

    public static void main(String[] args) {
        Integer[] sortedArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int targetValue = 7;
        int result = binarySearch(sortedArray, targetValue);
        if (result != -1) {
            System.out.println("Element found at index: " + result);
        } else {
            System.out.println("Element not found in the array.");
        }
    }
}

元素所在索引位置为6

分析:给定一个数组和待查找的目标值,每次循环都找先把数组一分为二,把中间值和目标值比较

如果中间值等于目标值直接返回,

中间值大于目标值,则说明目标值在左边界索引和中间值索引之间,把右边界索引调整为中间值索引减一的索引(因为小于中间值,所以不包含中间值索引)

中间值小于目标值,说明目标值在中间值索引和右边界索引之间,把左边界索引调整为中间值索引加一的索引(因为大于中间值,所以不包含中间值索引)

那么二分查找的时间复杂度到底怎么算出来的?

二分搜索最坏的情况就是折半一直找到最后一个元素,首先观察规律

开始时,是从n个元素中查找

第一次折半时,是从\frac{n}{2}个元素中查找

第二次折半时,是从\frac{n}{4}个元素中查找

假设第k次折半后只剩一个元素,即是从\frac{n}{2^k}个元素中查找

\frac{n}{2^k} = 1,即 n = ^{2^k},由对数定义知道 k = \log_{2}n,在计算机科学中如果没有特殊说明,\log n默认就是以2为底,即k=\log n

即操作k次才能找到最后一个元素,所以时间复杂度为O(\log n)

O(n)时间复杂度代码示例:

计算数组元素的和

public int sumOfArray(int[] nums) {
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }
    return sum;
}

查找数组中指定元素的索引

public int findIndexOf(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] == target) {
            return i;
        }
    }
    return -1; // 如果未找到,返回-1
}

删除链表中的重复节点

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

public ListNode deleteDuplicates(ListNode head) {
    ListNode current = head;
    while (current != null && current.next != null) {
        if (current.val == current.next.val) {
            current.next = current.next.next;
        } else {
            current = current.next;
        }
    }
    return head;
}

这些示例都具有O(n)的时间复杂度,因为每个算法的核心循环或操作都直接依赖于输入数据的大小n,即数据n多大,基本就需要循环多少次。
 

O(n\log n)时间复杂度代码示例

import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.IntStream;

public class SimpleQuickSort {

    public static void quickSort(int[] arr) {
       quickSortHelper(arr, 0, arr.length - 1);
    }

    private static void quickSortHelper(int[] arr, int low, int high) {
        if (low < high) {
            int pivotIndex = partition(arr, low, high);
            quickSortHelper(arr, low, pivotIndex - 1);
            quickSortHelper(arr, pivotIndex + 1, high);
        }
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] <= pivot) {
                i++;
                swap(arr, i, j);
            }
        }
        swap(arr, i + 1, high);
        return i + 1;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    // 测试代码
    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        System.out.println("Original array: " + Arrays.toString(arr));
        quickSort(arr);
        System.out.println("Sorted array: " + Arrays.toString(arr));
    }
}

以上代码是快速排序的一个递归实现

影响分析上面代码的时间复杂度的函数是quickSortHelperpartition

quickSortHelper方法采用了递归调用,最好情况下每次调用都能减少一半规模,类似于二分查找的时间复杂度计算方法,为O(\log n)

partition方法内部主要是采用了for循环,遍历整个数组,复杂度为O(n)

二者结合的时间复杂度为O(n\log n)

O(n^{2})时间复杂度代码示例

import java.util.HashMap;
import java.util.Map;

public class PairSums {

    public static void countPairSums(int[] arr) {
        int n = arr.length;
        Map<Integer, Integer> sumCounts = new HashMap<>();

        // 双重循环遍历数组中的每对元素
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                // 计算当前元素对的和
                int pairSum = arr[i] + arr[j];
                // 更新该和在map中的计数
                sumCounts.put(pairSum, sumCounts.getOrDefault(pairSum, 0) + 1);
            }
        }

        // 打印每种和及其出现的次数
        for (Map.Entry<Integer, Integer> entry : sumCounts.entrySet()) {
            System.out.println("Sum: " + entry.getKey() + ", Count: " + entry.getValue());
        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 5, 7, -1, 5};
        countPairSums(arr);
    }
}

以上代码计算一个数组中所有两两元素对的和,并统计这些和的出现次数

这段代码中,外层循环遍历数组的每个元素,内层循环则与外层循环的元素配对,计算它们的和,并使用哈希表统计所有不同和值的出现次数。由于存在两层循环,每层循环都依赖于数组长度n,因此总的时间复杂度为O(n^{2})。
 

O(n^{3}), O(2^{n}),O(n!)这三种时间复杂度较高,实际场景一般很少直接使用,都会寻找代替算法或者特定场景的优化算法,这里不做展示了

二、空间复杂度

空间复杂度用来衡量一个算法在执行过程中临时占用存储空间大小的量度。以下是一些常见的空间复杂度类别:

O(1) - 常数空间:
算法所需的存储空间不随输入数据大小的变化而变化,是固定大小的。例如,基本的数学运算或简单的变量赋值。

public class ConstantSpaceExample {

    // 方法用于计算两个整数的和
    public static int addTwoNumbers(int a, int b) {
        int sum = a + b; // 使用固定数量的变量来存储结果
        return sum;
    }

    public static void main(String[] args) {
        // 示例调用
        int result = addTwoNumbers(100, 200);
        System.out.println("两数之和为: " + result);
    }
}

分析:在这个例子中,无论是a、b还是sum变量,都是固定使用的,不会因为输入参数的大小而增加额外的存储需求,因此该函数的空间复杂度为O(1)。

O(\log n) - 对数空间:
空间需求与输入数据大小的对数成正比。例如,二分查找或平衡二叉树的遍历。

public class BinarySearch {
    
    // 二分查找函数
    public static int binarySearch(int[] sortedArray, int target) {
        int left = 0;
        int right = sortedArray.length - 1;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;
            
            // 如果目标值等于中间元素,返回索引
            if (sortedArray[mid] == target) {
                return mid;
            } else if (sortedArray[mid] < target) {
                // 目标值大于中间元素,更新左边界
                left = mid + 1;
            } else {
                // 目标值小于中间元素,更新右边界
                right = mid - 1;
            }
        }
        
        // 如果未找到目标值,返回-1
        return -1;
    }

    public static void main(String[] args) {
        int[] array = {1, 3, 5, 7, 9};
        int target = 5;
        int index = binarySearch(array, target);
        if (index != -1) {
            System.out.println("目标值 " + target + " 在数组中的索引是: " + index);
        } else {
            System.out.println("目标值 " + target + " 不在数组中");
        }
    }
}

分析:二分查找算法在每一步都将搜索范围减半,因此递归深度最多为\log n,其中n是数组的大小。由于递归调用栈的深度与\log n成正比,所以二分查找的空间复杂度是O(\log n)。

O(n) - 线性空间:
空间需求与输入数据大小成正比。例如,创建一个与输入数组大小相同的辅助数组。

public class LinearSpaceExample {

    // 创建新数组并复制原数组内容
    public static int[] copyArray(int[] originalArray) {
        int n = originalArray.length;
        int[] copiedArray = new int[n]; // 新数组的大小与原数组相同

        // 遍历原数组并将元素复制到新数组
        for (int i = 0; i < n; i++) {
            copiedArray[i] = originalArray[i];
        }

        return copiedArray;
    }

    public static void main(String[] args) {
        int[] original = {1, 2, 3, 4, 5};
        int[] copied = copyArray(original);

        System.out.println("Original Array: " + Arrays.toString(original));
        System.out.println("Copied Array: " + Arrays.toString(copied));
    }
}

分析:在这个例子中,copiedArray的大小与originalArray相同,因此所需的额外空间与输入数组的长度成正比,即O(n)。

O(n\log n) - 线性对数空间:
空间需求与输入数据的线性对数成正比。例如,归并排序的辅助数组。

O(n^{2}) - 平方空间:
空间需求与输入数据的平方成正比。例如,二维数组或在一些算法中创建的辅助矩阵。

O(n^{k}) - 高阶多项式空间:
空间需求与输入数据的k次方成正比,k > 1。较少见,但在某些算法中可能出现。

O(2^{n}) - 指数空间:
空间需求与输入数据的指数成正比。例如,递归深度特别深的情况。

O(n!) - 阶乘空间:
极少情况下,空间需求与输入数据的阶乘成正比,通常发生在极端的组合问题中。

空间复杂度分析通常关注算法运行时临时存储的需求,不包括输入数据本身占用的空间。在实际应用中,优化空间复杂度通常是减少算法运行时内存消耗的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1648192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LSS(Lift, Splat, Shoot)算法解析

1.简介 LSS(Lift, Splat, Shoot) 是一个比较经典的自下而上的构建BEV特征的3D目标检测算法&#xff0c;通过将图像特征反投影到3D空间生成伪视锥点云&#xff0c;通过Efficientnet算法提取云点的深度特征和图像特征并对深度信息进行估计&#xff0c;最终将点云特征转换到BEV空…

JETBRAINS IDES 分享一个2099通用试用码!IDEA 2024 版 ,支持一键升级

文章目录 废话不多说上教程&#xff1a;&#xff08;动画教程 图文教程&#xff09;一、动画教程激活 与 升级&#xff08;至最新版本&#xff09; 二、图文教程 &#xff08;推荐&#xff09;Stage 1.下载安装 toolbox-app&#xff08;全家桶管理工具&#xff09;Stage 2 : 下…

盲盒小程序怎么做?盲盒创业

盲盒作为当下的新兴行业&#xff0c;从出现就备受年轻消费者的追捧&#xff0c;成为了我国发展前景巨大的行业之一。盲盒市场不仅吸引了众多消费者&#xff0c;同时也吸引了更多的创业者&#xff0c;成为了一大创业新模式。 盲盒小程序是一种线上盲盒销售模式&#xff0c;以社…

赋能企业数字化转型 - 易点易动固定资产系统与飞书实现协同管理

在当前瞬息万变的商业环境下,企业如何借助信息化手段提升管理效率,已经成为摆在各行各业面前的紧迫课题。作为企业数字化转型的重要一环,固定资产管理的信息化建设更是不容忽视。 易点易动作为国内领先的企业资产管理服务商,凭借其全方位的固定资产管理解决方案,助力众多企业实…

Baidu Comate:你的智能编码助手,编程效率倍增的秘密武器

Baidu Comate智能编码助手 Baidu Comate 智能编码助手简单介绍安装使用查看Comate插件功能智能代码提示使用飞浆和百度智能小程序进行智能问答使用AutoWork插件实现二次函数图像的生成引用Comate知识库存在的问题结束语 Baidu Comate 智能编码助手简单介绍 Baidu Comate&#x…

信创 | 2023年中国信创产业深度研究报告(完整版)

信创产业研究报告 免责声明&#xff1a;本文资料来源于“第一新声”&#xff0c;版权归原作者所有。如涉及作品版权问题&#xff0c;请与我们联系&#xff0c;我们将在第一时间协商版权问题或删除内容&#xff01; 获取文中相关的PPT资料&#xff0c;请关注文末公众号“程序员…

创意自我介绍视频制作软件有哪些?

创意自我介绍视频制作软件 在制作创意自我介绍视频时&#xff0c;有许多软件可供选择。以下是一些推荐的软件&#xff1a; 乐秀视频剪辑&#xff1a;这是一个被8亿用户选择的视频剪辑、视频制作与Vlog剪辑工具。它提供了丰富的视频编辑功能&#xff0c;帮助用户制作出高质量的…

海云安受邀参加诸子云 4.27南京「金融互联网」私董会

4月27日&#xff0c;“安在新媒体网安用户行业活动”第四期私董会在南京顺利举办。活动以“金融&互联网”为主题&#xff0c;邀请十余位业内资深的甲方用户以及典型厂商代表。摒弃传统的议题分享&#xff0c;采取“随时问答&#xff0c;自由讨论”的形式&#xff0c;提问题…

Android 状态栏WiFi图标的显示逻辑

1. 状态栏信号图标 1.1 WIFI信号显示 WIFI信号在状态栏的显示如下图所示 当WiFi状态为关闭时&#xff0c;状态栏不会有任何显示。当WiFi状态打开时&#xff0c;会如上图所示&#xff0c;左侧表示有可用WiFi&#xff0c;右侧表示当前WiFi打开但未连接。 当WiFi状态连接时&#x…

如何应对访问国外服务器缓慢的问题?SDWAN组网是性价比之选

访问国外服务器缓慢通常由以下原因造成&#xff1a; 1、政策限制&#xff1a;我国管理互联网&#xff0c;限制部分国外网站和服务器&#xff0c;以维护国家安全稳定。 2、技术障碍&#xff1a;国内与国际互联网的网络架构和协议存在差异&#xff0c;可能导致数据传输不兼容。 …

F.softmax(cls) + 1e-4

这个代码段中的 softmax 操作结合了一个微小的常数&#xff0c;这个常数通常被称为平滑化参数。softmax 函数将原始的分类输出转换为概率分布&#xff0c;其公式如下&#xff1a; 在实践中&#xff0c;当某些分类得分特别大时&#xff0c;softmax 函数会将对应的概率接近于 1&a…

filezilla 上传文件到服务器

1. 下载windows ftp客户端 https://filezilla-project.org/index.php 点击左侧的 Download FileZilla Client All platforms 默认安装。 2. 双击打开 filezilla 按图中填写&#xff0c;并上传。 主机名: 10.20.89.* 用户名: zhuzh 密码: *** 端口: 22 点击 快速连接&#…

10个必备功能打造跨境电商平台开发全攻略

在跨境电商行业中&#xff0c;拥有一个高效且功能完善的电商平台至关重要。针对跨境电商平台开发&#xff0c;我们将介绍十个必备功能&#xff0c;帮助您全面打造优秀的跨境电商平台。 关键功能一&#xff1a;多语言支持 跨境电商平台开发的首要功能之一是多语言支持。提供多…

探秘Flex布局下子元素宽度超出的那些烦心事

嘿&#xff0c;小伙伴们&#xff01;你们有没有遇到过用Flex布局的时候&#xff0c;子元素的宽度莫名其妙地超出了父元素的情况&#xff1f;别着急&#xff0c;今天我就来给大家揭秘这个问题的来龙去脉&#xff0c;以及一些解决方案。让我们一起来深入探讨&#xff01; 发现问…

73. 矩阵置零/54. 螺旋矩阵

73. 矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 思路&#x…

OurBMC开源大赛企业获奖队伍专访来啦!

精彩纷呈的 OurBMC 开源大赛已告一段落&#xff0c;经历为期四个月的实战&#xff0c;各个参赛队伍也积淀了丰富的实践经验与参赛心得。本期&#xff0c;社区特别邀请 OurBMC 开源大赛获奖企业团队分享「走进OurBMC开源大赛&#xff0c;共同践行开放包容、共创共赢的开源精神」…

WordPress建站从入门到精通,跨境电商建站教程

详情介绍 课程内容:WordPress建站从入门到精通,跨境电商建站教程 - 百创网-源码交易平台_网站源码_商城源码_小程序源码 1-WordPress网站的优势 2-如何用Builtwith识别网站的搭建技术? 3-如何配置站长信息? 4-如何使用WordPress虚拟后台 5-如何选择一个好的网站域名?…

【Osek网络管理测试】[TG4_TC5]唤醒条件

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT验证DUT睡眠后被唤醒&#xff0c;并再次睡眠能否…

流畅的python-学习笔记_一等函数

函数对象 函数也是对象&#xff0c;操作可像对象一般操作 高阶函数 高阶函数指接受参数为函数&#xff0c;或返回函数的函数 不少高阶函数在py3已经有了替代品。map&#xff0c; filter可通过生成式实现&#xff0c;reduce&#xff08;在functools里&#xff09;可通过sum实…

Web3智能物联网:科技连接的未来世界

在当今科技飞速发展的时代&#xff0c;Web3智能物联网正逐渐成为人们关注的焦点。随着区块链技术的不断成熟和普及&#xff0c;以及物联网的普及和应用&#xff0c;Web3智能物联网作为二者的结合&#xff0c;将为未来的数字世界带来革命性的变化。本文将深入探讨Web3智能物联网…