数据结构与算法 - 基础

本文首发于 个人博客

程序 = 数据结构 + 算法

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

对自然数从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


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【React 轮子】文本溢出后显示展开/收起按钮

/** hooks* 用于文本展示时判断是否展示 展开/收起按钮 &#xff08;包含监听 文本变化/页面尺寸变换&#xff09;* param { string } text 需要展示的文本* param { number } maxLength 文本最大展示行数* param { number } lineHeight 文本行高 (单位 px) */ import React, …

交通工具图像分割系统:全面扶持小白

交通工具图像分割系统源码&#xff06;数据集分享 [yolov8-seg-vanillanet&#xff06;yolov8-seg-C2f-Parc等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Global A…

pta题目

1.查询至少生产两种不同的计算机(PC或便携式电脑)且机器速度至少为133的厂商 AC: select distinct(pd.maker) --去重查询 from product pd where pd.type in (个人电脑, 便携式电脑) --题目上要求的&#xff0c;至少一个&#xff0c;in是从里面选择 and --这里也是model其实相…

windows下用CMake构建使用protobuf的应用,编译使用VS2022

最近构建一个使用protobuf的应用&#xff0c;踩了不少坑&#xff0c;在此记录一下 一、编译protobuf protobuf只提供源码&#xff0c;没有编译好的库文件给使用造成一定的障碍&#xff08;差评&#xff09;。所以c应用中使用protobuf的第一步是用cmake对protobuf进行构建。 1.…

计组-主存的分类和编址,随机存取(RAM)和只读(ROM)存储器

随机和只读存储器这2类是有着不同的功能的 像我们的内存就属于随机存取存储器&#xff08;RAM&#xff09;&#xff0c;其特点就是当内存一旦断电时&#xff0c;内存里面的所有数据都将被清除掉&#xff0c;无法保存下来&#xff0c;即一断电信息就会丢失 而ROM在断电后是可以…

【electron+vue3】使用JustAuth实现第三方登录(前后端完整版)

实现过程 去第三方平台拿到client-id和client-secret&#xff0c;并配置一个能够外网访问回调地址redirect-uri供第三方服务回调搭建后端服务&#xff0c;引入justauth-spring-boot-starter直接在配置文件中定义好第一步的三个参数&#xff0c;并提供获取登录页面的接口和回调…

一次线程池使用错误导致的问题

记录一次服务线程数量异常问题的排查过程 背景 通过监控发现一个服务的线程数异常多 同期CPU 内存 网络连接都没有什么异常。 排查 第一个反应就是查看线程栈 "pool-2493-thread-3" #3718833 prio5 os_prio0 tid0x00007f1610041000 nid0x38bff6 waiting on con…

我为何要用wordpress搭建一个自己的独立博客

我在csdn有一个博客&#xff0c;这个博客是之前学习编程时建立的。 博客有哪些好处呢&#xff1f; 1&#xff0c;可以写自己的遇到的问题和如何解决的步骤 2&#xff0c;心得体会&#xff0c;经验&#xff0c;和踩坑 3&#xff0c;可以转载别人的好的技术知识 4&#xff0c;宝贵…

java毕业设计之基于Bootstrap的常州地方旅游管理系统的设计与实现(springboot)

项目简介 基于Bootstrap的常州地方旅游管理系统的设计与实现有下功能&#xff1a; 基于Bootstrap的常州地方旅游管理系统的设计与实现的主要使用者分为用户功能模块和管理员功能模块两大部分&#xff0c;用户可查看景点信息、景点资讯等&#xff0c;注册登录后可进行景点订票…

面试经典 150 题:189、383

189. 轮转数组 【参考代码】 class Solution { public:void rotate(vector<int>& nums, int k) {int size nums.size();if(1 size){return;}vector<int> temp(size);//k k % size;for(int i0; i<size; i){temp[(i k) % size] nums[i];}nums temp; }…

mysql--多表查询

一、联合查询 作用&#xff1a;合并结果集就是把两个select语句的查询结果合并到一起&#xff01; 合并结果集有两种方式&#xff1a; UNION&#xff1a;合并并去除重复记录&#xff0c;例如&#xff1a;SELECT * FROM t1 UNION SELECT * FROM t2&#xff1b; UNION ALL&a…

什么是严肃游戏,严肃游戏本地化的特点是什么?

“严肃游戏”是一种交互式数字体验&#xff0c;不仅用于娱乐&#xff0c;还用于教育、培训或解决问题。与主要关注乐趣和参与度的传统游戏不同&#xff0c;严肃游戏的目标不仅仅是娱乐&#xff0c;比如教授特定技能、模拟现实生活场景或提高对重要问题的认识。它们用于医疗保健…

ADI常规SHARC音频处理器性能对比

1、 ADSP-2156x:是基于SHARC+ DSP架构的单核32位/40位/64位浮点处理器,不仅具有灵活的音频连接性和性能可扩展性,还提供多个引脚兼容版本(400MHz至1GHz)和多种片内存储器选项,数据手册链接:https://www.analog.com/media/en/technical-documentation/data-sheets/adsp-2…

springboot 整合 抖音 移动应用 授权

后端开发&#xff0c;因为没有JavaSDK&#xff0c;maven依赖&#xff0c;用到的是API接口去调用 抖音API开发文档 开发前先申请好移动应用&#xff0c;抖音控制台-移动应用 之后还需要开通所有能开通的能力 拿到应用的 clientKey 和 clientSecret&#xff0c;就可以进入开发了 …

Python 三维图表绘制指南

Python 三维图表绘制指南 在数据可视化中&#xff0c;三维图表可以更直观地展示数据之间的关系&#xff0c;尤其是当数据具有多个维度时。Python 提供了多个库来绘制三维图表&#xff0c;其中最常用的就是 Matplotlib。本文将介绍如何使用 Matplotlib 绘制三维图表&#xff0c…

Node.js:Express 服务 路由

Node.js&#xff1a;Express 服务 & 路由 创建服务处理请求req对象 静态资源托管托管多个资源挂载路径前缀 路由模块化 Express是Node.js上的一个第三方框架&#xff0c;可以快速开发一个web框架。本质是一个包&#xff0c;可以通过npm直接下载。 创建服务 Express创建一…

计算机网络-以太网小结

前导码与帧开始分界符有什么区别? 前导码--解决帧同步/时钟同步问题 帧开始分界符-解决帧对界问题 集线器 集线器通过双绞线连接终端, 学校机房的里面就有集线器 这种方式仍然属于共享式以太网, 传播方式依然是广播 网桥: 工作特点: 1.如果转发表中存在数据接收方的端口信息…

学生成绩查询系统设计与实现

学生成绩查询系统设计与实现 1. 系统概述 学生成绩查询系统是一个基于PHP和SQL的Web应用程序&#xff0c;旨在为学校提供一个高效的学生成绩管理和查询平台。该系统可以帮助教师录入成绩、学生查询成绩、管理员管理用户和成绩数据&#xff0c;提高教育管理的效率和透明度。 2…

Rust 力扣 - 2653. 滑动子数组的美丽值

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们遍历长度为k的的窗口 因为数据范围比较小&#xff0c;所以我们可以通过计数排序找到窗口中第k小的数 如果小于0&#xff0c;则该窗口的美丽值为第k小的数如果大于等于0&#xff0c;则该窗口的美丽值为0 题…

VisualStudio远程编译调试linux_c++程序(二)

前章讲述了gdb相关&#xff0c;这章主要讲述用VisualStudio调试编译linux_c程序 1&#xff1a;环境 win10 VisualStudio 2022 Community ubuntu22.04 2:安装 1>vs安装时&#xff0c;勾选 使用c进行linux 和嵌入式开发 (这里以vs2022为例) OR VS安装好了&#xff0c; 选择工…