数据结构与算法 - 基础

news2024/11/26 10:27:21

本文首发于 个人博客

程序 = 数据结构 + 算法

其实很多同学知道数据结构与算法很重要,但是却不明觉厉。 这里我们看一个简单的题:

对自然数从1到100的求和

最简单的设计无非是:

void addNum () 
{    
  int total = 0;    
  for (int i = 1; i <= 100; i ++) {     
     total += i;  
  }   
 printf("total is %d \n",total);// 5050}

没毛病,但是哥根廷的数学家 高斯 在其9岁的时候就发明了一个快速计算等差数列求和的小技巧 (1+100,2+99,3+98.....),总共50 对101,结果5050。其公式可以归纳为:

不论n多大,我们只用算一次就可以得出结果,而上面的循环却要循环n次,n很小无所谓,如果几万几十万这个时间消耗不言而喻,由此可见数据结构与算法确实很重要。

数据

数据结构中最基本的5个概念:数据、数据元素、数据项、数据对象,他们整体构成数据结构。

image

比较抽象,我们用代码来展示如下:

struct Teacher {
    char *name;
    char *sex;
    int age;
};

int main(int argc, const char * argv[]) {
    struct Teacher t1;          // 数据元素
    struct Teacher tArray[10];  // 数据对象( 一组数据元素组成 )
    t1.name = "Mary";           // 数据项
    t1.sex = "female";          // 数据项
    t1.age = 23;                // 数据项
    return 0;
}

数据元素之间不是独立的,存在特定的关系,这些关系即结构,数据结构指的就是数据对象中数据元素之间的关系。

结构

从视角的不同作以区分,我们将数据结构分为两种类型:逻辑结构物理结构

逻辑结构

逻辑结构分为:集合结构线性结构树形结构图形结构

  • 集合结构

集合结构中的数据元素除了同属一个集合外,彼此之间没有其他关系。

  • 线性结构

线性结构中的数据元素之间都是一对一的关系,从图中也可以看出他们像一条线一样把各个元素连起来,常用的线性结构有:链表队列数组 等等。

  • 树形结构
树状.jpg

树形结构中的元素是呈现一对多的关系,常见的树有:二叉树B树哈夫曼树 等等。

  • 图形结构

图形结构之间的元素是多对多的关系,常见的图形结构有: 矩阵 等。

物理结构

物理结构其实就是存储结构,就是存储到计算机中的形式。数据存储的结构才真正反映了数据存在的样式,也反映了数据元素之间的逻辑关系,如何设计数据的存储以及相互之间的关系才是数据结构的关键。

  • 顺序存储

我们知道计算机中的内存都是连续的,如上图所述,1-6个元素按照顺序存储的方式存到内存里,比如第一个元素的内存地址是 0x000001 假设我们这个顺序表中每个元素占1个位置,那么很容易得到第二个元素的地址是 0x000002,以此类推很容易知道第六个元素的地址是 0x000006

  • 链式存储

这里我画了一个简单的图,大概描述了一下链式存储在内存中是如何体现的。当我们的元素在内存中是散乱的,也就是他们的地址之间没有一定的规律,这个时候就要靠我们的指针去标记位置了,比如我们把 元素2 的物理地址 0x000019 存储到 元素1 中去,那么每两个元素之间就会有一定的相互绑定的关系,这就是链式存储的基本逻辑。

通过上述我们会发现,顺序存储的结构查找会很容易,因为直接按照顺序对应的索引就能对应找到相应的内存地址,而链式存储则不行,不过链式存储的结构对于数据的增删就特别快了,因为他们之间的关系靠的是指针,而不是内存地址的偏移。

算法

算法是解决特定问题步骤的描述,在计算机中表现为解决特定问题的一系列代码

数据结构脱离算法,或者算法脱离数据结构都是无法进行的,因为算法是基于数据结构进行的,只有数据结构而没有算法那么数据结构存在就没有意义了。

程序 = 数据结构 + 算法

算法的特性

  • 输入输出

  • 有穷性 :当然不能是无限循环了...

  • 确定性 : 每一步都是明确的,不能出现模棱两可选择。

  • 可行性

算法的设计要求

  • 正确性

  • 可读性

  • 健壮性

  • 时间效率高和存储量低 :其实这才是算法的精髓,研究算法无非就是要提高效率和降低存储。

时间空间复杂度

通常我们用程序代码执行的次数作为算法时间复杂度的衡量,通常我们用 大O 法来进行标记。

  • 用常数1取代运行时间中的所有常数

  • 各种复杂度相加,只保留最高阶 n+2n+log(n)+n^2 -----> n^2

  • 如果最高阶存在且不是1,则去掉这个项相乘的常数 O(5logn) -----> O(logn)

    常数阶

    通常用O(1) 来表示

//1+1+1 = 3
void testSum1(int n){
    int sum = 0;                //执行1次
    sum = (1+n)*n/2;            //执行1次
    printf("testSum2:%d\n",sum);//执行1次
}

不管n是多少,这个地方都只用执行1次,所以n不会影响其时间复杂度,O(1)

线性阶
void add2(int x,int n){
    for (int i = 0; i < n; i++) {
        x = x+1;
    }
}

可以发现这个for循环会执行n次,所以其时间复杂度为 O(n)

对数阶
int count = 1;
while(count < n){
    count = count * 2;
}

上述算法换个写法就是2^x = n 就可以得到 x = log2n,我们说过常数如果不是1都可以去掉,最终结果就是 O(logn)

平方阶
// x=x+1; 执行n*n次
void add3(int x,int n){
    for (int i = 0; i< n; i++) {
        for (int j = 0; j < n ; j++) {
            x=x+1;
        }
    }
}

外层循环n次,内层循环n次,加在一起就是 n*n = n^2 次,所以其时间复杂度为 O(n^2)

O(1)<O(logn)< O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

其实我们只用关注前面的复杂度就行了,如果你的算法时间复杂度像后面的那几位靠齐,也就没啥好说的了,指数级的增长还是蛮恐怖的。

空间复杂度

算法设计有一个重要原则: 空间 时间 权衡

算法的空间负责度是通过计算算法所需的存储空间实现,算法空间复杂度的计算公式是:s(n) = n(f(n)) 其中n为问题的规模,f(n) 为语句关于n所占存储空间的函数。

我们通过一个简单的例子大概了解一下空间复杂度:

int n = 5;
    int a[10] = {1,2,3,4,5,6,7,8,9,10};

    //算法实现(1)
    /*
    算法(1),仅仅通过借助一个临时变量temp,与问题规模n大小无关,所以其空间复杂度为O(1);
    */
    int temp;
    for(int i = 0; i < n/2 ; i++){
        temp = a[i];
        a[i] = a[n-i-1];
        a[n-i-1] = temp;
    }

    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",a[i]);

    }


    //算法实现(2)
    /*
     算法(2),借助一个大小为n的辅助数组b,所以其空间复杂度为O(n).
    */
    int b[10] = {0};
    for(int i = 0; i < n;i++){
        b[i] = a[n-i-1];
    }
    for(int i = 0; i < n; i++){
        a[i] = b[i];
    }
    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",a[i]);

    }

需要注意的是:算法的空间复杂度并不是整个算法占用内存的空间,而是该算法在实现的时候所需的辅助空间。

研究算法的最终目的是要提高程序运行的效率,我们还想降低程序运行所占用的内存等等,这两个一个是时间维度,一个是空间唯独。一个好的算法并不是说一定要时间复杂度低,也不一定要空间占用小,这些东西要根据项目实际情况去均衡。希望这篇文章能够将数据结构与算法的基础知识讲清楚。

最后编辑于:2024-10-27 15:08:58


喜欢的朋友记得点赞、收藏、关注哦!!!

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

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

相关文章

算法简介:动态规划

动态规划 1. 动态规划2. 案例2.1 旅游行程最优化 1. 动态规划 背包问题&#xff1a;背包可以容纳的重量是4磅&#xff0c;吉他为1磅&#xff0c;价值1500元&#xff1b;音响为4磅&#xff0c;价值3000元&#xff1b;笔记本电脑为3磅&#xff0c;价值为2000元。如何在背包中放入…

GPU 学习笔记四:GPU多卡通信(基于nccl和hccl)

文章目录 一、前沿1.1 背景回顾1.2 XCCL在AI通信架构中的位置和作用 二、英伟达GPU通信 nccl2.1 NCCL简介2.2 通信模式2.2.1 通信模式分类2.2.2 通信模式分析2.2.3 通信nccl编程实例 2.3 NCCL通信算法2.3.1 环形算法 ring algorithm2.3.2 树形算法 tree algorithm 防止遗忘和后…

最新PHP校园源码系统开发(多客社区校园系统源码全套APP源码附搭建教程)

最新PHP校园源码系统开发、多客社区校园系统源码以及全套APP源码附搭建教程的需求 一、最新PHP校园源码系统开发 技术栈选择&#xff1a; 后端&#xff1a;PHP&#xff08;建议使用PHP 7.2或更高版本&#xff09;数据库&#xff1a;MySQL&#xff08;建议使用MySQL 5.6或更高版…

Netty 组件介绍 - ByteBuf

直接内存&堆内存 ByteBuf buffer ByteBufAllocator.DEFAULT.heapBuffer(10);ByteBuf byteBuf ByteBufAllocator.DEFAULT.directBuffer(10); 组成 ByteBuf维护了两个不同的索引&#xff0c;一个用于读取&#xff0c;一个用于写入。 写入 内存回收 堆内存使用的是JVM内…

Java项目实战II基于Java+Spring Boot+MySQL的高校办公室行政事务管理系统(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在高等教育…

word mathml 创建粗体字母快捷键

在 mathml 中达到latex中 \mathbf{A} 的效果 由于word本身不支持这个命令&#xff0c;所以打算用快捷键实现 快捷键的功能是加粗光标前一个字目 1. Alt F8 打开宏&#xff0c;如果打不开可以尝试 Alt Fn F8 2. 输入 BoldPreviousCharacter 新建宏&#xff1a; Sub Bold…

redis实现分布式锁,go实现完整code

Redis分布式锁 Redis 分布式锁是一种使用 Redis 数据库实现分布式锁的方式&#xff0c;可以保证在分布式环境中同一时间只有一个实例可以访问共享资源。 实现机制 以下是实现其加锁步骤&#xff1a; 获取锁 在 Redis 中&#xff0c;一个相同的key代表一把锁。是否拥有这把锁&…

flink 自定义kudu connector中使用Metrics计数平均吞吐量,并推送到自定义kafkaReporter

文章目录 前言1. Registering metrics2. Metrics 的类型2.1 counter2.2 Gauge2.3 Histogram2.4 meter 3. 指标划分3.1 指标所属的范围3.2 默认所属 4. 自定义kudu connector中使用Metrics4.1 sink算子继承RichFunction4.2 注册指标4.3 计数逻辑4.4 自定义Reporter&#xff0c;推…

柯桥日语培训|N1常考语法:~(よ)うが/(よ)うと——“无论……都……”

&#xff5e;&#xff08;よ&#xff09;うが&#xff0f;&#xff08;よ&#xff09;うと 接续&#xff1a;動意向形&#xff0f;イ形→かろう&#xff0f;名、ナ形→だろう・であろう&#xff0b;が&#xff0f;と 说明&#xff1a;表示假定条件的逆接&#xff0c;无论前项如…

一个基于Zookeeper+Dubbo3+SpringBoot3的完整微服务调用程序示例代码

一、关于 Dubbo3 的一些优化改进介绍 Dubbo3 的官方文档地址&#xff1a; https://cn.dubbo.apache.org/zh-cn/overview/what/overview/ 其针对一些问题进行了优化和改变。个人整理3个小的方面&#xff1a; 1. 在服务注册方面使用 DubboService 注解&#xff0c;不再使用 Servi…

电能表预付费系统-标准传输规范(STS)(33)

6.5.4.4 Key rotation process 按键旋转过程 The entire key is rotated one bit position to the left as illustrated in Figure 15.整个密钥向左旋转一个位&#xff0c;如图15所示。 6.5.4.5 Worked example to generate TokenData for a TransferCredit token using the S…

时序数据库是什么:概念、特点与分类简析

时序数据与时序数据库的“保姆级”科普&#xff01; 作为将数据价值转化为产能能效的“核心大脑”&#xff0c;数据库的发展依然处于加速期&#xff0c;面向不同数据类型的数据库类型也在不断增加。 在众多细分领域数据库类型中&#xff0c;伴随制造业数字化转型的行业趋势和多…

【创建型】单例模式

单例模式使用的场景&#xff1a;需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即&#xff1a;重量级对象)&#xff0c;但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等) 1. 饿汉式&#xff08;静态常量&#xf…

6.0、静态路由

路由器最主要的功能就是转发数据包。路由器转发数据包时需要查找路由表&#xff08;你可以理解为地图&#xff09;&#xff0c;管理员可以直接手动配置路由表&#xff0c;这就是静态路由。 1.什么是路由&#xff1f; 在网络世界中&#xff0c;路由是指数据包在网络中的传输路…

工业通信网关的各项功能解析-天拓四方

在工业自动化和智能制造的浪潮中&#xff0c;工业通信网关作为连接工业现场与互联网的重要桥梁&#xff0c;发挥着至关重要的作用。它不仅实现了不同网络协议之间的转换&#xff0c;还在数据采集、设备控制、网络管理等方面展现出强大的功能。 一、协议转换功能 工业通信网关…

数据结构与算法——Java实现 53.力扣938题——二叉搜索树的范围和

生命的意义 在于活出自我 而不是成为别人眼中的你 —— 24.11.3 938. 二叉搜索树的范围和 给定二叉搜索树的根结点 root&#xff0c;返回值位于范围 [low, high] 之间的所有结点的值的和。 示例 1&#xff1a; 输入&#xff1a;root [10,5,15,3,7,null,18], low 7, high 15 …

TensorRT-LLM的k8s弹性伸缩部署方案

Scaling LLMs with NVIDIA Triton and NVIDIA TensorRT-LLM Using Kubernetes | NVIDIA Technical Blog 一共涉及4个k8s组件&#xff1a; 1. Deployment&#xff1a;跑起来N个pod&#xff1b;指定NVIDIA官方的triton&trt-llm的docker image&#xff0c;指定好model放在哪个…

高亮无惧烈日,强力巨彩租赁屏点亮户外“视”界

在户外显示领域&#xff0c;一款性能出色、适应性强、维护便捷的租赁屏无疑是众多主办方和广告商的首选。强力巨彩旗下的幻云系列租赁屏具备画面清晰、无水波纹、性能稳定、高亮度等诸多优势&#xff0c;可应用于各大户外显示场所&#xff0c;是户外租赁屏市场的明星产品。   …

批量删除redis数据【亲测可用】

文章目录 引言I redis客户端基础操作key的命名规则批量查询keyII 批量删除key使用连接工具进行分组shell脚本示例其他方法III 知识扩展:控制短信验证码获取频率引言 批量删除redis数据的应用: 例如缓存数据使用了新的key存储,需要删除废弃的key。RedisTemplate的key序列化采…

Mysql开发规范

开发规范 对象命名 命名规范的对象&#xff0c;是指数据库SCHEMA、表TABLE、字段COLUMN、索引INDEX、约束CONSTRAINTS等 【强制】凡是需要命名的对象&#xff0c;其标识符不能超过30个字符【强制】名称必须以英文字母开头&#xff0c;不得以 _(下划线) 作为起始和终止字母【…