文章目录

  • 什么是数据结构?
    • 数据
    • 数据元素
    • 数据结构
    • 数据之间的结构关系
      • 逻辑结构
        • 1.特点
        • 2.分类
      • 存储结构
        • 顺序存储
        • 链式存储
  • 什么是算法?
    • 算法的特性
    • 评价算法好坏的方法
    • 时间复杂度
      • 大O的渐进表示法
      • 常见时间复杂度计算
    • 空间复杂度

什么是数据结构?

数据

数据即信息的载体,是能够输入到计算机中并且能被计算机识别、存储和处理的符号总称。文字、视频、音频等等,都是数据,数据并不仅仅指数字。

数据元素

数据元素是数据的基本单位,又称之为记录(Record)。一般数据元素由若干基本数据项组成。

数据结构

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

我们生活中遇到的一些问题,计算机不懂,我们需要把问题抽象成一种数据模型(逻辑结构),这种模型是计算机可以理解的,然后存储到计算机中(存储结构),让计算机去帮助我们解决。

数据之间的结构关系

数据元素与数据元素之间的关系分为两种:逻辑结构、存储结构。

1.逻辑结构

逻辑结构表示数据之间的抽象关系(如邻接关系、从属关系等),按每个元素可能具有的直接前趋个数和直接后继个数,将逻辑结构分为“线性结构”和“非线性结构”两大类(与数学模型相关)。

2.存储结构

存储结构是指在计算机中的具体实现方法,分为顺序存储方法、链接存储方法、索引存储方法、散列存储方法。

逻辑结构

1.特点

描述数据结构中数据元素之间的关系,是从具体问题中抽象出来的数学模型,是独立于计算机存储器的。

2.分类

主要分为两大类:线性结构、非线性结构。

线性结构

简单地说,线性结构是n个数据元素的有序(次序)集合。

线性结构的特点:

  • 集合中必存在唯一的一个"第一个元素";
  • 集合中必存在唯一的一个"最后的元素";
  • 除最后元素之外,其它数据元素均有唯一的"后继";
  • 除第一元素之外,其它数据元素均有唯一的"前驱"。

树形结构

又叫层次结构,树形结构指的是数据元素之间存在着“一对多”的树形关系的数据结构。

在树形结构中,树根结点没有前驱结点,其余每个结点有且只有一个前驱结点。叶子结点没有后续结点,其余每个结点的后续节点数可以是一个也可以是多个。

图形结构

又叫网状结构,在图结构中任意两个元素之间都可能有关系,也就是说这是一种多对多的关系。

其他结构

除了以上几种常见的逻辑结构外,数据结构中还包含其他的结构,比如集合等。

存储结构

计算机的存储设备有:寄存器(辅助cpu运算,临时存储的)、高速缓存、主存储器(内存)、外存储器(硬盘、磁盘)等。

1.特点

数据的逻辑结构在计算机存储器中的映象,存储结构是通过计算机程序来实现的,因而是依赖于具体的计算机语言的,也就是指数据是如何在计算机中存储的。

2.分类

顺序存储

顺序存储(Sequential Storage):将数据结构中各元素按照其逻辑顺序存放于存储器一片连续的存储空间中

优点:结构紧凑,查找速度快,支持随机访问(下标访问)。

缺点:头部、中间插入删除数据,速度慢。

链式存储

链式存储(Linked Storage):将数据结构中各元素分布到存储器的不同点,用记录下一个结点位置的方式建立它们之间的联系,由此得到的存储结构为链式存储结构。

优点:分散,不会占用整块空间,对空间利用更加灵活和随意(有空就插入存储),减少了已有数据的移动;

缺点:遍历查找速度慢 ,不支持随机访问(下标访问)。

什么是算法?

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

算法和数据结构密切相关,算法设计取决于选定的逻辑结构,算法实现依赖于采用的存储结构。

算法的特性

  • 有穷性 :算法执行的步骤(或规则)是有限的;
  • 确定性 :每个计算步骤无二义性;
  • 可行性 :每个计算步骤能够在有限的时间内完成;

评价算法好坏的方法

算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。

时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。

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

时间复杂度

在计算机科学中,算法的时间复杂度是一个函数(带有未知数的数学函数),它定量描述了该算法的运行时间。

一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。相同的代码在不同的机器上运行的时间可能不同,但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。

没办法算时间,那我们算什么呢?一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度

// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(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;}printf("%d\n", count);
}

Func1执行的基本次数为:
   F(N) = N^2 + 2*N + 10

当N的值不同时,执行的次数不同,N越大后两项对结果的影响越小。所以,实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法

大O的渐进表示法

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

推导大O阶方法:

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

这样得到的结果就是大O阶。

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

   O( N^2)

通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。

常见时间复杂度计算

计算如下几个例题的时间复杂度

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

   F(N) = 2*N+10

1取代常数加法,只保留最高阶,去掉常数系数,那么时间复杂度为:

   O(N)

// 计算Func3的时间复杂度?
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);
}

F(N) = M+N

一般情况下时间复杂度计算时未知数都是使用N,但是也可以是M,K等。

如果M远大于N,是O(M)
如果N远大于M,是O(N)
M和N差不多大,可以是O(M)或者O(N)

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

F(N) = 100
用常数1代替所有加法的常数

O(1)

注意:O(1)并不是代表算法运行1次,而是常数次。

// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character );

strchr是库函数,在字符串中查找某个字符

while(*str)
{if(*str == charcter){return str;}else{++str;}
}

这个算法分情况:
最好:第一个字符就是要找的字符O(1)
最坏:最后一个字符是要找的字符O(N)
平均:中间查找到要找的字符O(N/2)

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

最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)

例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到

当一个算法随着输入的不同,时间复杂度不同,那么时间复杂度做悲观预期,看最坏的情况。

// 计算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;}
}

n个数
第一趟:n-1对
第二趟:n-2对
第三趟:n-3对

第n-1趟:1对

等差数列

F(N) = n*(n-1)/2
O(N^2)

算法的时间复杂度的计算不能只看有几层循环,而要看思想。

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n-1;while (begin < end){int mid = begin + ((end-begin)>>1);if (a[mid] < x)begin = mid+1;else if (a[mid] > x)end = mid;elsereturn mid;}return -1;
}

对于二分查找,
最好的情况::第一次就找到O(1)

最坏的情况是:最后才找到,最后剩下一个数就是我们要查找的数,每次查找排除掉一半数据,n是数组元素个数,
n/2/2/2…/2 = 1

n/(2^m) = 1

m是查找的次数
所以查找的次数是:log⁡2n\log_2^nlog2n​
时间复杂度是:O(log⁡2n\log_2^nlog2n​)

二分查找是很牛逼的算法

N个数中查找   大概查找次数
1000          10
100w          20
10亿           30

但是,二分查找要求必须有序,排序的代价很大。

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

递归:不停的调用栈帧
递归算法时间复杂度:递归次数 * 每次递归调用的次数

所以阶乘递归的时间复杂度是O(N)

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


右边的一些递归会提前结束,需要减去X项

F(N) = 202^020 + 212^121 + 222^222 + … + 2N−1−X2^{N-1} - X2N−1−X
等比数列求和,时间复杂度为:O(2N)O(2^N)O(2N)

斐波那契数列的递归写法完全是一个实际中没有用的算法,太慢了。实际中,斐波那契数列用循环来写。

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时额外占用存储空间大小的量度(算法本身需要的变量,不需要计算,比如形参不需要计算) 。

空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。

空间复杂度计算规则基本跟时间复杂度类似,也使用大O渐进表示法。

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

// 计算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(1)

额外的变量是exchange,end,i,空间是可以重复利用不累计的;时间一去不复返累计的.

内存循环每次结束,i就销毁了,下次循环重新创建i,所以一共使用了3个额外的变量,所以空间复杂度是O(1)。

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t n)
{if(n==0)return NULL;long long * fibArray = (long long *)malloc((n+1) * sizeof(long long));fibArray[0] = 0;fibArray[1] = 1;for (int i = 2; i <= n ; ++i){fibArray[i] = fibArray[i - 1] + fibArray [i - 2];}return fibArray;
}

空间复杂度是O(N)
时间复杂度是O(N)。

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{if(N == 0)return 1;return Fac(N-1)*N;
}

每次调用都会建立栈帧,空间复杂度是O(N).

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


Fib(N) = Fib(N-1) + Fib(N-2)
先算Fib(N-1),再算Fib(N-2),Fib(N-1)又层层递归,当Fib(N-1)计算完毕后,所有空间释放掉,Fib(N-2)又重新开辟空间。
空间可以重复利用,不累计;时间一去不复返,是累计的。上图中左边的递归结束后,空间释放,右边的递归再重复利用之前的空间。

空间复杂度是O(N)。

常见复杂度对比

一般算法常见的复杂度如下:


本章完。

数据结构和算法基本概念相关推荐

  1. Python数据结构与算法—基本概念

    数据结构基本概念 数据结构: 程序 = 数据结构 + 算法 1.数据:即信息的载体,是能够输入到计算机中并且能被计算机识别.存储和处理的符号总称. 2.数据元素:是数据的基本单位,又称之为记录(Rec ...

  2. 【数据结构与算法基础概念】

    #数据结构与算法 课程目标 课程内容 一.数据结构与算法概述 1.1 什么是数据结构 1.2 数据结构分类 逻辑结构的分类 物理结构的分类 1.3什么是算法 1.4算法初体验 二. 算法分析 2.1 ...

  3. 数据结构与算法基本概念总结

    数据结构:1数据的逻辑结构(线性结构.树形结构.图形结构) 2数据的存储结构(顺序.连接.索引.散列) 3数据的运算 算法:     是为了求解一个问题所遵循的.被清楚地指定的简单指令的集合 学习算法 ...

  4. jquery删除数组中的某个元素下标越界_Java数据结构和算法(二)—数组

    目录 1.Java数组介绍 2.用类封装数组实现数据结构 3.分析数组的局限性 上篇博客我们简单介绍了数据结构和算法的概念,对此模糊很正常,后面会慢慢通过具体的实例来介绍.本篇博客我们介绍数据结构的鼻 ...

  5. Java数据结构和算法(二)——数组

    上篇博客我们简单介绍了数据结构和算法的概念,对此模糊很正常,后面会慢慢通过具体的实例来介绍.本篇博客我们介绍数据结构的鼻祖--数组,可以说数组几乎能表示一切的数据结构,在每一门编程语言中,数组都是重要 ...

  6. Java数据结构和算法(一)——简介

    本系列博客我们将学习数据结构和算法,为什么要学习数据结构和算法,这里我举个简单的例子. 编程好比是一辆汽车,而数据结构和算法是汽车内部的变速箱.一个开车的人不懂变速箱的原理也是能开车的,同理一个不懂数 ...

  7. Java数据结构和算法(二):数组

    上篇博客我们简单介绍了数据结构和算法的概念,对此模糊很正常,后面会慢慢通过具体的实例来介绍.本篇博客我们介绍数据结构的鼻祖--数组,可以说数组几乎能表示一切的数据结构,在每一门编程语言中,数组都是重要 ...

  8. java 算法_Java 浅谈数据结构和算法

    以前不管自己还是朋友在面试java工程师岗位的时候,都会被问到这样的问题: "介绍下java中的数据结构和算法". 很多朋友被问到的时候发现无从下口,甚至特别是一些初级java工程 ...

  9. python中文教程github_GitHub - Virile-Tao/python_data_structures_and_algorithms: Python 中文数据结构和算法教程...

    Python 算法与数据结构视频教程 课程简介 数据结构和算法是每个程序员需要掌握的基础知识之一,也是面试中跨不过的槛.目前关于 Python 算法和数据结构的系统中文资料比较欠缺, 笔者尝试录制视频 ...

最新文章

  1. 红帽将召开“开放你的世界”在线论坛
  2. Node.js有了新的管理者
  3. Python之路---函数进阶
  4. 洛谷P1069 细胞分裂 数学
  5. 中国自由软件推广先锋的自述,心潮澎湃的一往无前,一定要看!作者:洪峰
  6. 12306的数据库设计
  7. 几个常用快速无损压缩算法性能比较
  8. gif透明背景动画_最好用的GIF制作手机app-GIF豆豆--手机版ae
  9. IT项目经理如何正确做好项目实施
  10. 自然语言处理中的文本聚类
  11. 软件工程师典藏 C# 程序开发范例宝典 第3版
  12. android sqlite 存储对象,SQLite存储对象
  13. 【VUE+elementUI+JAVA】elementUI的<el-table>组件VUE自定义排序+后台排序含分页完整版
  14. XStream 简介
  15. 新一代区块链游戏:超越炒作的NFT和玩游戏盈利
  16. (转)jQuery Validation Plugin客户端表单证验插件
  17. 使用CANalyzer搭建LIN通信网络
  18. 音频合并无缝衔接怎么弄?这篇文章教会你
  19. mysql的url地址什么意思_URL是什么意思?
  20. 3D三维向量的单位化、正规化、标准化推导及代码实现

热门文章

  1. 敏捷方法论:理解敏捷测试的完整指南
  2. 进销存管理对于企业的意义
  3. 各种脚距对应的端子型号
  4. 无敌寂寞的系统一键重装软件推荐!
  5. Android Studio 汉化包
  6. Unity 之 ShaderGraph 实现超级炫酷的溶解效果入门级教程
  7. 我们的系统检测到您的计算机网络中存在异常流量。此网页用于确认这些请求是由您而不是自动程序发出的。
  8. vmware桥接模式-无法内网通-克隆机要删除的文件-ssl
  9. Unity-3D捕鱼达人小游戏开发 —— 炮台子弹发射
  10. [Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(上)