对数据结构的初步认识

在这里插入图片描述

前言:

牛牛开始更新数据结构的知识了.本专栏后续会分享用c语言实现顺序表,链表,二叉树,队列,排序算法等相关知识,欢迎友友们互相学习,可以私信互相讨论哦!

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏: 🍔🍟🌯 c语言初阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解数据结构的入门知识,时间复杂度与空间复杂度,以及一些对学习数据结构的建议.
金句分享:
✨最快的脚步不是冲刺,而是坚持!✨

目录

  • 前言:
    • 1、数据结构是什么?
    • 2、数据结构应该怎么学呢?
  • 算法效率如何衡量?
    • 一、 时间复杂度
      • 大O的渐进表示法
      • 时间复杂度的练习:
      • 1.1 常见的时间复杂度:
      • 1.2 冒泡排序的时间复杂度
      • 1.3 "二分查找"的时间复杂度
      • 1.4 递归的时间复杂度:
      • 常见量级的比较图
    • 二、空间复杂度

1、数据结构是什么?

数据结构+算法=程序.

数据结构(Data Structure):是计算机存储、组织数据方式,指相互之间存在一种或多种特定关系的数据元素的集合。
例如后面会提到的顺序表,链表这些线性数据结构,还有后面的二叉树树形数据结构等.

算法(Algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果.
例如:排序算法.

数据结构与算法对于一个程序员是很重要的,不论对你思考问题的方式还是对你编程的思维都会有很大的好处。同时在找工作时算法也是一个重要考点之一.

2、数据结构应该怎么学呢?

1.多多练习代码.
在这里插入图片描述

数据结构的学习并不简单,需要多锻炼代码能力,最怕偷懒,很多时候头脑虽然理解了,但是动起手来写代码会忽略很多细节,导致程序出错,不能光有思路,而代码能力却实现不了就很尴尬了.

2.多画图(这个强烈推荐)

除了代码能力需要锻炼以外,很重要的一点是要有思路,通过画图辅助,可以很好地帮助我们找到思路和理解数据结构中的很多思想,切忌上来就开始码代码,这样对于简单的问题可能可以解决,但是对于稍微复杂的问题可能会让你头痛(大佬除外😂😂),很容易被绕进去,陷入痛苦的调试找bug环节.
画图会让提供给我们清晰的思路,同时,即使出现了bug,也可以很快的找到,清晰可见.写代码只是用于实现思路,思路清晰,代码写起来并不困难.

3.刷题
刷题会锻炼我们的思考能力,解题是一种很灵活的事情.一方面可以巩固我们学的基础知识,另一方面可以拓展思维.
最后,坚持学习才是最重要的.
在这里插入图片描述

算法效率如何衡量?

对于一个问题,可以有很多解法,那怎样衡量一个算法的好坏呢?
比谁的代码更简洁吗?
算法的效率主要考虑两点:1.时间复杂度. 2.空间复杂度

一个算法在编译生成可执行文件后,运行时会耗费时间资源和空间(内存)资源
从时间和空间两个维度来衡量一个算法的好坏是比较合理的,这就是时间复杂度空间复杂度

时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

一、 时间复杂度

时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间.

但是从理论上说,这个只有将代码进行测试,并统计时间才能知道.并不能通过计算得到.
但对于每一个算法,我们都去跑一下,这未免显得有些麻烦,我们可以通过算法中的代码估计运行大概的时间,看看属于哪一个量级来衡量它的效率.

算法中的基本操作的执行次数,为算法的时间复杂度。
即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度

理论不是很理解的话,我们来点实际的,找几段代码算算吧!

🌰小试牛刀
你能算出在test1中++count语句最终被执行了多少次吗?

void test1(int N)
{int count = 0;//1for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){++count;}}//2for (int k = 1; k < N; k*=2){++count;}//3for (int k = 0; k < 2 * N; k++){++count;}//4int a = 100;while (a--){++count;}printf("%d\n", count);
}

答案:
1: N * N
2: log2 N
3. 2*N
4. 100

则我们可以抽象出这样的数学公式:

   test(N)=N2 +log2 N+2N+100

大O的渐进表示法

计算机的运行速度是很快的,对于时间复杂度的计算,没有必要追求那么精确,对于那些对结果影响不大的项,我们可以忽略不计.如果我们只保留N2这一起决定因素的项.

大O阶方法计算方法:

  • 1、用常数1取代运行时间中的所有加法常数
  • 2、在修改后的运行次数函数中,只保留最高阶项
  • 3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

使用大O的渐进表示法以后,test1的时间复杂度为:

(O)N ^ 2

即使是100N系数也应当去掉,因为当数据足够大的时候100的影响并不大.
只要是常数,都应当是1.
也许你会认为100很大或者100000很大.但是,要看和谁比,如果是和10亿比呢?一万亿比呢?
那我们打个比方:

你觉得你们学校大吗?还行.
你所在的城市大吗?算大吧!
你觉得我们的祖国大吗?地大物博,确实大.

但是,与太阳系相比呢?与银河系相比呢?这就显得很渺小了,沧海之一粟罢了.
所以当数据量足够大的时候,常数项和那些影响不大的忽略不计.

时间复杂度的练习:

1.1 常见的时间复杂度:

例1:

// 计算Test2的时间复杂度?
void Test2(int N)
{int count = 0;for (int i = 0; i < 2 * N ; i++){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);}

例2:

// 计算Test3的时间复杂度?
void Test3(int N, int M)
{int count = 0;for (int i = 0; k < M; i++){++count;}for (int i = 0; i < N ; i++){++count;}printf("%d\n", count);
}

例3:

// 计算Test4的时间复杂度?
void Test4(int N)
{int count = 0;for (int i = 0; i < 10000; i++){++count;}printf("%d\n", count);
}

答案:
这三个例子的时间复杂度还是很好计算的.
例1:

2N+10用大O表示法表示时间复杂度为O(N).

例2:

基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)
如果m和n相等则可以表示为O(N),如果一方远大于另一方,则可以用大的一方表示,记住是远大于,即不在一个量级.

例3:

基本操作语句被执行了常数次.这用大O表示法表示时间复杂度为O(1).

那试着分析TargetNum函数的时间复杂度.
TargetNum函数是用于在一个数组中查找目标值的函数,找到了就返回目标值的地址,没找到就返回NULL.

int* TargetNum(int* arr,int n, int num)
{for (int i = 0; i < n; i++){if (arr[i] == num)//找到目标数字则返回数字的地址{return arr + i;}}return NULL;
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int num = 0;scanf("%d", &num);int*ret =TargetNum(arr, sz, num);if (ret == NULL){printf("该目标值不存在");}printf("%d ", *(ret));if (ret + 1 <= &arr[sz - 1]){printf("%d ", *(ret + 1));}
}

此时就让人有些疑惑了,这个函数的时间复杂度似乎不是固定的.在1~n的范围之间的那如何确定它的时间复杂度呢?
在这里插入图片描述

分析一下:
最好情况时(第一个数就是目标值): O(1).
平均情况时:O(n/2).
最坏情况时:O(n).

那我们选择哪种情况比较合理呢?
那我们讲一个小故事吧.

假如你是一名高中生,你刚经历期末考试,晚上,老师只公布了部分答案.
你可以确保自己可以拿到60分,有剩余的40分中,你按照以往的每次考试的经验来看,不出意外20分是可以拿到的.
此时回到家中,老爸问你能考多少分?考多少分老爸就奖励你多少钱,嘿嘿.🍭🍭🍭
你会说80(平均情况)分吗?还是会选择100分(最好情况)呢?
万一食言了呢?
咱一般都会选择最坏的情况,那样即使出现了意外,我们也没有说错,而不出意外时,无论是平均还是最好情况,我们都会比较高兴的.

这里牛牛也想告诉大家,结局未定之前,不要过分高看自己,降低期望,当然也不要自卑,继续努力,继续前行,保持对生活的热爱,生活也会拥抱你的!🍭🍭🍭

回到正题,此时我们会选择最坏的情况作为时间复杂度,即TargetNum函数的时间复杂度是O(n).

1.2 冒泡排序的时间复杂度

大家还记得c语言时学的冒泡排序吗?

// 计算BubbleSort的时间复杂度?
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(0)或者O(1).
首先呢,没有O(0)这一说法,这几乎不可能,其次这里最好的情况也不是O(1),为什么呢?
如果数组有序,那不就不需要排序吗?那不就是O(1)吗?

其实,即使数组有序,我们也需要循环遍历一遍这个数组,才能知道有序,计算机不是人哦,他不能看一眼就知道有序,而且就算是人,当数据量比较大的时候人一眼也看不出来是否有序吧!
总结:
最好情况:O(N)
最坏情况:O(N2)

1.3 "二分查找"的时间复杂度

int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n-1;while (begin <= end){int mid = (begin+end)/2;//找到中间值if (a[mid] < x)//如果该值比中间值大,则直接从中间值的后半部分里面找{begin = mid+1;}else if (a[mid] > x)//如果该值比中间值小,则直接从中间值的前半部分里面找{end = mid-1;}else//找到了return mid;}//没找到返回-1,这里设置为-1也许有些不合理,可以使用逻辑值.return -1;
}

判断一次(与中间值比较),就可以去掉一半的值.即该算法一次N的值就会等于N/2.
不难得出该算法的时间复杂度是O(logn2).

补充知识:logn2经常省略写成log2甚至lg2

"二分查找"看起来平平无奇,但其实是个隐藏的大佬啊!
大家知道这些量级的差距有多大吗?log2是很可怕的量级,速度极快.
看图感受一下吧!

友友们感受到二分查找的厉害了吧!

遗憾的是,二分查找的前提是数据得是有序的,否则他无法实现一次排除一半.而数据往往是无序的,并且有些特殊的数据还不允许排序,排序会破坏数据的.
这也就让二分查找无计可施了,纸老虎罢了.😂😂😂

1.4 递归的时间复杂度:

// 计算阶乘递归Fac的时间复杂度?
int Fac(int N)
{if(0 == N)//递归的结束条件{return 1;}return Fac(N-1)*N;
}

递归的时间复杂度计算主要是根据其递归的层数来决定.
该算法每次递归N就-1,则递归的次数为N.
故算法的时间复杂度为:O(N).

// 计算斐波那契递归Fib的时间复杂度?
int Fib(int N)
{if(N < 3)return 1;return Fib(N-1) + Fib(N-2);
}

此算法,每次经过递归都需要再递归2 * N次.
在这里插入图片描述
则此算法的时间复杂度为O(2n).

常见量级的比较图

大O的渐进表示法:
在这里插入图片描述

二、空间复杂度

空间复杂度并不是重点,现如今,一般情况下我们的时间的价值比空间的价值要高的多.
定义:

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 。
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。

注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定

例题:
还是拿冒泡排序来举例吧!

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;}
}

为了实现冒泡排序我们定义了int exchange = 0;以及Swap(&a[i-1], &a[i]);函数中的int tmp.
这项常数个临时变量,故空间复杂度为O(1).

例2:

int Fac(int N)
{if(0 == N)//递归的结束条件{return 1;}return Fac(N-1)*N;
}

同样递归的空间复杂度由开辟的栈帧个数(每次递归开辟一次栈帧)决定,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N).

好了,数据结构的初步认识就到这里啦!后续牛牛会继续更新数据结构的相关知识.
如果文章对大家有用的话记得一键三连哦!💗💗💗
如果文章中有部分错误之处,可以私信牛牛,互相讨论哦!!!

在这里插入图片描述

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

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

相关文章

dash shell 和 zsh shell 简单介绍

一、dash shell dash shell不能识别bash shell的所有脚本编程功能 dash shell支持expr命令和双圆括号方法&#xff0c;但不支持方括号方法。 bash shell的test命令允许你使用双等号()来测试两个字符串是否相等。这是为了照顾习惯在其他编程语言中使用这种格式的程序员而加上去…

【简写Mybatis】02-注册机的实现以及SqlSession处理

前言 注意&#xff1a; 学习源码一定一定不要太关注代码的编写&#xff0c;而是注意代码实现思想&#xff1a; 通过设问方式来体现代码中的思想&#xff1b;方法&#xff1a;5W1H 源代码&#xff1a;https://gitee.com/xbhog/mybatis-xbhog&#xff1b;https://github.com/xbh…

%00截断 [GKCTF 2020]cve版签到

打开题目 F12之后在Headers中发现hint 两者结合利用零字符截断使get_headers()请求到本地127.0.0. 结合链接 构造 ?urlhttp://127.0.0.1%00www.ctfhub.com 必须以123结尾 ?urlhttp://127.0.0.123%00www.ctfhub.com 得到flag 知识点&#xff1a; PHP中get_headers函数 g…

解析ChatGPT Plus相比chatgpt3.5有哪些优势

「ChatGPT Plus」提供更出色的对话体验和更广泛的应用能力&#xff0c;学生可以用来写作、职场人也可以用来写计划书、策划书等等&#xff0c;并且问它一些问题比搜索引擎好用多了简直。但普通人使用起来有一点门槛&#xff0c;并且升级4.0也难住了许多爱好者。 ChatGPT主要功能…

【Excel PDF 系列】EasyExcel + iText 库

你知道的越多&#xff0c;你不知道的越多 点赞再看&#xff0c;养成习惯 如果您有疑问或者见解&#xff0c;欢迎指教&#xff1a; 企鹅&#xff1a;869192208 文章目录 前言转换前后效果引入 pom 配置代码实现定义 ExcelDataVo 对象主方法EasyExcel 监听器 前言 最近遇到生成 …

微信小程序蓝牙通信HC08

总结这两天研究的蓝牙串口。人话版资料不多&#xff0c;主要靠翻别人的仓库和文档。 单片机部分&#xff0c;与蓝牙串口通信是通过串口。比我想的要简单&#xff0c;小程序部分&#xff0c;有非常多的服务和特征&#xff0c;而且人话版资料不多。 如果本文有什么问题&#xf…

代理模式(Proxy Pattern)

定义 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;其目的是为其他对象提供一个代理或占位符&#xff0c;以控制对这个对象的访问。代理类通常在客户端和目标对象之间起到中介的作用&#xff0c;用于控制对目标对象的访问&#xff0c;并在必…

Kafka是如何保证消息不丢失

Apache Kafka通过多种机制来确保消息不丢失&#xff0c;包括数据复制&#xff08;Replication&#xff09;、持久化&#xff08;Persistence&#xff09;、确认机制&#xff08;Acknowledgments&#xff09;、幂等生产者&#xff08;Idempotent Producer&#xff09;、事务性发…

AI绘画工具合集,让想象触手可及!

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

【JS】【Vue3】【React】获取鼠标位置的方法:JavaScript、Vue 3和React示例

目录 使用JavaScript原生方法在Vue 3中获取鼠标位置在React中获取鼠标位置 随着Web应用程序的复杂性不断增加&#xff0c;获取用户交互信息变得越来越重要。其中&#xff0c;获取鼠标位置是一项常见的任务&#xff0c;可以用于实现各种交互效果&#xff0c;如拖拽、悬停提示等。…

通过curl 请求接口 /usr/bin/curl: Argument list too long

因为要发送的json数据为图片的base64码&#xff0c;使用该命令时提示参数过长&#xff0c;解决方法使用如下命令 curl -X POST -d data.json http://example.com/api 将要发送的报文体放入data.json 然后执行 使用来从文件中读取数据而不是直接在命令行上写入大量数据 示例&…

【目标检测新SOTA!v7 v4作者新作!】YOLO v9 思路设计 + 全流程优化 + 手把手训练自己数据

YOLO v9 思路复现 全流程优化 手把手训练自己数据 提出背景&#xff1a;深层网络的 信息丢失、梯度流偏差YOLO v9 设计逻辑可编程梯度信息&#xff08;PGI&#xff09;&#xff1a;使用PGI改善训练过程广义高效层聚合网络&#xff08;GELAN&#xff09;&#xff1a;使用GELAN…

java面试:分布式事务理论基础(CAP原理、BASE理论、本地事务)

文章目录 引言I 基础知识1.1 事务(Transaction)1.2 本地事务1.3 分布式事务II 分布式理论2.1 CAP原理2.2 BASE理论2.3 刚柔事务2.4 解决分布事务模型2.5 2PC2.6 3PC2.7 TCC(Try-Confirm-Cancel)补偿事务引言 分布式事务:实现跨服务事务回滚,需要用到分布式事务。

华为数通方向HCIP-DataCom H12-821题库(单选题:481-500)

第481题 以下关于基于SD-WAN思想的EVPN互联方案的描述,错误的是哪一项? A、通过部署独立的控制面,将网络转发和控制进行了分离,从而实现了网络控制的集中化 B、通过对WAN网络抽象和建模,将上层网络业务和底层网络具体实现架构进行解耦,从而实现网络自动化 C、通过集中的…

四、分类算法 - 决策树

目录 1、认识决策树 2、决策树分类原理详解 3、信息论基础 3.1 信息 3.2 信息的衡量 - 信息量 - 信息熵 3.3 决策树划分的依据 - 信息增益 3.4 案例 4、决策树API 5、案例&#xff1a;用决策树对鸢尾花进行分类 6、决策树可视化 7、总结 8、案例&#xff1a;泰坦尼…

深度学习手写字符识别:推理过程

说明 本篇博客主要是跟着B站中国计量大学杨老师的视频实战深度学习手写字符识别。 第一个深度学习实例手写字符识别 深度学习环境配置 可以参考下篇博客&#xff0c;网上也有很多教程&#xff0c;很容易搭建好深度学习的环境。 Windows11搭建GPU版本PyTorch环境详细过程 数…

stable diffusion学习笔记 手部修复

图片手部修复原理 某张图片在生成后&#xff0c;仅有手部表现不符合预期&#xff08;多指&#xff0c;畸形等&#xff09;。这种情况下我们通常使用【局部重绘】的方式对该图片的手部进行【图生图】操作&#xff0c;重新绘制手部区域。 但是仅采用重绘的方式也很难保证生成的…

python爬虫实战:获取电子邮件和联系人信息

引言 在数字时代&#xff0c;电子邮件和联系人信息成为了许多企业和个人重要的资源&#xff0c;在本文中&#xff0c;我们将探讨如何使用Python爬虫从网页中提取电子邮件和联系人信息&#xff0c;并附上示例代码。 目录 引言 二、准备工作 你可以使用以下命令来安装这些库&a…

将文件从windows传入到ubuntu

实现效果图 2.方法&#xff1a; 2.1打开 Ubuntu 的终端窗口&#xff0c;然后执行如下命令来安装 FTP 服务 输入&#xff1a;sudo apt-get install vsftpd 等待软件自动安装&#xff0c;安装完成以后使用如下 VI 命令打开/etc/vsftpd.conf&#xff0c;命令如下&#xff1a;su…

6.二元操作符

平凡也就两个字: 懒和惰; 成功也就两个字: 苦和勤; 优秀也就两个字: 你和我。 跟着我从0学习JAVA、spring全家桶和linux运维等知识,带你从懵懂少年走向人生巅峰,迎娶白富美! 关注微信公众号【 IT特靠谱 】,每天都会分享技术心得~ 1.二元操作符 用户可以使用这些操作符对时…