《大话数据结构》的观后感和笔记总结

《大话数据结构》的观后感和笔记总结

写在开头的话:程序设计=算法+数据结构,这本是我在大一寒假研读的数据结构,每次读书记录一点心得和笔记,之后也会有相关算法的书籍博客会出。而这本书的代码是基于C语言,我的理解可能是从python出发。

目录

  1. 数据结构绪论
  2. 算法
  3. 线性表
  4. 栈与队列
  5. 查找
  6. 排序

Day 1
1.数据结构绪论(基础概念)
1.1数据本身
①数据:
描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。其包括整型、实型等数值类型,还包括字符及声音、图像、视频等非数值类型。所以个人理解数据是客观生活中可以被具体量化下来显示出的具体事物。
所以我们对这些数据或是符号要么做数值间的运算,或是非数字数据的处理,如文字或是图像处理,比如图像通过编码将其转化为字符型数据。数据:可输入计算机+可被处理

②数据元素:
是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理。数据元素也被称为记录。数据元素往往是数据分析的着眼点。按照上面所举的例子,数据结构就是研究构成数据的元素之间的关系,即是人与人之间的联系。

③数据项:
一个数据元素可以自若干个数据项组成,是最小的不能分割的。

理解:对上面三个概念打个比方,数据像是人类所包含的所有相关数据(这个有点类似于Python的类和对象),那么其中的数据元素代表了你,然后数据项就像是你的年龄、身高、体重等更细小的分支,类似于二叉树中往下更小的分叉。

④数据对象:
是性质相同的数据元素的集合,是数据的子集。即一类相似的人的集合,比如说有相同的爱好等。在日常处理这些数据对象的时候因为其有很大的相似性所以简称其为数据。

备注:
用Python来解释的话数据就相当于类,数据对象就是类所实例化的性质相同的对象组,而数据元素就是实例化的一个个对象,那么数据项就相当于是对象的实际属性。

数据结构
相互之间存在一种或多种特定关系的数据元素的集合。个人认为数据结构侧重在特殊的关系之间。

1.2 关系层面(也就是数据结构中的特殊关系)
按照视点的不同,数据结构又分为逻辑结构和物理结构。那么什么是视点呢?就是从你的视角,通俗来讲就是从不同方面或是角度来看,说的装逼一点可以称作视点。

1.2.1逻辑结构:指数据对象中数据元素之间的相互关系。

具体分为:

  • 集合结构(同属于一个集合)
  • 线性结构(一一对应的关系)
  • 树形结构(一对多的层次)
  • 图形结构 (多对多)

1.2.2物理结构(存储结构)
指数据的逻辑结构在计算机中的存储形式。就像网络协议中的物理层,就是具体与计算机硬件相连接的那个层次(解释物理)。

  • 数据元素的存储性质有两种:顺序存储结构和链式存储结构。
  • 顺序存储结构十分容易理解,学过语言的同学应该都理解,比如内存条一个一个往下排数据。就像排队一样按顺序排好再储存。
  • 链式存储结构像C语言中的指针,每一个数据都有他的地址,那么当用指针访问地址时就可以得到对应的数据。因此,这时候可以不用排队,随时等待叫号就好了。

1.3 数据类型(指一组性质相同的值的集合及定义在此集合上的一些操作的总称)

  • 特别的,在C语言中,数据类型根据取值的不同分为两类:原子类型和结构类型。其实说到底都是给自己想分的类给一个别称。

  • 原子类型:是不可以再分解的基本类型,包括整型、实型、字符型等。低层面

  • 结构类型:自若干个类型组合而成,是可以再分解的。例如,整型数组是由若干整型数据组成的。高层面

而在高级语言中我们不会去关系具体CPU是怎么帮我运算的。而我们只是考虑接近自然语言或是人类所知的运算或是处理规则来进行数据处理。所以我们可以把数据类型抽象出来,因此就有了抽象数据类型。

1.4 抽象数据类型(Abstract DataType即 ADT)
吐槽:呵呵,抽象的概念本来就很抽象好吧

  • 其实在我们生活中有许多就是抽象的数据类型,只是我们没有发现而已,比如整型就是一个抽象的数据类型,因为在不同的语言我们有不同的型式或是语法去规定.但无论怎么规定,整型在我们脑海中的意思都是固定的,这也是为什么有些语言在刚开始初学的时候有些基础内容十分相似,能让人快速上手的原因。
  • 其次,抽象的数据类型不一定需要按照语法的相关规定,由自己制定的照样可行,那些规定只不过是普适度高些罢了.
  • 一个抽象数据类型定义一个数据对象、数据对象中各数据元素之间的关系及对数据元素的操作。比如说超级马里奥之中马里奥身体的操作。走和跳,这也是抽象数据类型的一种.
  • 总结:抽象数据类型体现了程序设计中问题分解、抽象和信息隐藏的特性。即把我们的问题分解成为一个个小部分,并建立对应的模块,将相应的模块功能作为一个单元进行一定的封装。

以下是描述抽象数据类型的标准格式:

ADT 抽象数据类型名
Data 
数据元素之间逻辑关系的定义
Operation
    操作一
        初始条件
        操作结果描述
    操作二
    ...
    操作n
    ...
endADT

小结
这一部分我学到了数据相关的定义,数据结构和数据类型(特殊的是抽象数据类型)。即明白了数据本身的定义,组成和分解部分,关系和性质。

Day 2
2. 算法
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列并且每条指令表示一个或多个操作。(指令可以是计算机指令,也可以是我们的自然语言)

  • 作用:将算法是为了更好地让人理解数据结构。而且算法和数据结构一般结合在一起,缺一不可。

2.1算法的特性
有一定计算机基础应该对此了熟于心:算法具有五个基本特性即输入、输出、有穷性、确定性和可行性。(在此不做过多赘述)

2.2 算法的设计要求
这个要求就是评价一个算法的好坏标准,评价分为以下四个维度

  • 正确性:是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。
    通常分为一下几个层次
  1. 算法程序没有语法错误。
  2. 算法程序对于合法的输入数据能够产生满足要求的输出结果。
  3. 算法程序对于非法的输入数据能够得出满足规格说明的结果。
  4. 算法程序对于精心选择的,甚至刁难的测试数据都有满足要求的输出结果
    一般以第三层次为标准判断
    其实平常我们在根据简单问题设计程序的时候考虑的就是算法的正确性,考虑自己有没有把所有的情况都写进去,有没有一些特殊的情况是自己漏看的。
  • 可读性(阅读、理解、交流)
    这个和Python的语言宗旨有相似的地方,为什么我们说Python简单,因为很大程度上这个语言十分贴近我们的自然语言,使我们理解的门槛大大降低,所以上手会很快。当然我们在写代码也要注意这点,特别是当自己的代码要给别人阅读的时候。
  • 健壮性:当输入数据不合法时,算法也能做出相关处理, 不是产生异常或莫名其妙的结果(有点类似于普适性)
  • 时间效率高和存储量低
    也就是算法在执行时所占的时间资源和空间资源要小。

2.3 算法效率的度量方法

  • 事后统计方法:这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。

但这种方法有几个明显的缺陷:1)必须事先写好程序(所花费时间和精力多);2)时间的比较依赖计算机硬件和软件等环境因素;3)测试算法的数据规模大小很难判断。所以我们一般不用事后统计法。

  • 事前分析估算法:在计算机程序编制前,依据统计方法对算法进行估算。
    个用高级程序语 编写的程序在计算机上运行时所消耗的时间取决于下列因素:
  1. 算法采用的策略、 方法。
  2. 编译产生的代码质量。
  3. 问题的输入规模。
  4. 机器执行指令的速度。
    第1条当然是算法好坏的根本,第2条要软件来支持,而第4条要看硬件性能。

也就是说抛开这些与计算机硬件、软件有关的因素,一个程序的运行时间,依赖于算法的好坏和问题的输入规模。所以我们在计算时间是统计的就是基本的操作步骤。

我们撇开来谈一谈输入规模,也可以称作问题的输入规模n,注意输入是针对计算机来说的,所以这个规模大小指的不是数据的个数多少,而是数据在空间存储的内存大小,只是一般输入情况下我们把输入规模等价为输入的数据个数。但当输入大数的时候我们的输入规模就比单纯的输入数据大很多。因为计算机是二进制存储的,而一般我们的数据都是十进制,一个十进制数a,那么转化为二进制是有[log(2)a]+1,那么一个包括m位十进制的数组的存储容量大小就是<=m[log(2)a(max)]+m,进行模糊处理那么就是m(其实为什么log可以约去我保留疑问),但是当m十分大的时候我们就不能忽略之前的log,所以当大数参与进来时,有一些输入规模会比我们想象的要大些,比如说两个大数的相加那么此时的时间复杂度就不是O(1)这么简单,应该是log(2)n。因此就有了时间复杂度和空间复杂度一说。

2.4 时间复杂度和空间复杂度
学过算法的朋友想必对这两个名词并不陌生,我们常提的是前者即O(n)

  • 时间复杂度(最坏时间复杂度)定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析 T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度也就是算法的时间量度,记作: T (n) = O(f (n)) (采用了大O记法)它表示随时间规模的增大,算法执行时间的增长率和f (n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度,其中T(n) 是问题规模n的某个函数。

推导大O阶的方法:1)常数1取代加法常数;2)只保留参数本身,省去其系数;3)只保留最高阶;4)可数出具体的数字都是O(1),之后再用数学方法,推导出一共要执行的基本操作个数,这里我们一般只专注与n有关的循环

其中有:常数阶、线性阶、平方阶、对数阶、nlogn阶、 立方阶、指数阶、阶乘阶等(其中只有多项式阶是我们在有限时间内可以完成的,所以我们在优化算法的时候一定要将自己的算法往耗时间小的阶层上靠。

  • 算法的空间复杂度:通过计算算法所需的存储空间来实现,算法空间复杂度的计算公式记作:S(n)= O(f(n)) ,其中,n为问题的规模, f(n)为语句关于存储空间的函数。一般空间复杂度怎么算,举个例子,你给三个变量赋予值,那么无论你怎么改变变量的数值,空间都只占据那么些,因为在开辟新的存储空间时放入新的值时,在Python中没有用到的就被清理了,所以为O(1),而如果你一开始所开辟的是n个小空间,比如创建了一个n个数的数组,那么此时是O(n)

有时候我们可以用空间来取代时间,帮助减少时间损耗。

回顾:算法的定义——》算法的五大基本特性——》算法的四个设计要求即评价标准——》算法效率的度量方法——》时间复杂度和空间复杂度

Day 3
3. 线性表
这是数据结构中最简单且最常用的一种结构。

3.1 定义
线性表(list):每个或多个数据元素的有限序列。
要点:有限、有序、元素同级、开头元素无前驱,结尾元素无后继,中间元素前驱和后继有且仅有一个、是一对一的关系

线性表元素的个数是线性表的长度,当数据个数为0时,是空表。
在较复杂的线性表中一个数据可以由若干个数据项组成。

3.2 线性表的抽象数据类型
说实话,线性表的结构和Python中的list十分像。因为我们在不同语言对线性表的表达和语法都不同而抽象的数据类型就是从这些类型中抽象出一个可以概括所有类型的线性表。这就是抽象,从具体的类型中抽象出来。我们研究的就是这个不随语言改变而变的一种本质。

而针对抽象数据类型的基本操作有:

  1. 新建
  2. 判断是否为空线性表
  3. 清空线性表
  4. 将线性表L中第i个位上置的元素返回给一个变量
  5. 查找是否有给定的那个元素
  6. 在第i位置插入一个新元素
  7. 删除第i位置的一个元素,并返回给一个变量
  8. 返回线性表的元素个数
    其实对Python或者C比较熟悉的朋友可以直接看上面的操作反馈具体的语法,比如第7个在Python中就是pop,而这些较为人性化的操作其实也是由一些更基本的操作组合而来,可以算是一个比较小的“封装”。

3.3 线性表的顺序存储结构
3.3.1 概念
我们想要存储一个数据其实需要几个属性,就如同大学舍友去找座位坐一样,我们肯定得找到一个起始的位置之后在一个一个排列坐下去,那么我们挑座位的时候肯定要挑大于四个座位的空地(我们大学一个宿舍四个人)。综上我们出现了三个变量:宿舍人数(线性表的length)、能坐的空地位置个数(最大的存储容量)、存储空间的起始位置。但有时会出现新的问题,如果我的舍友是一个大胖子,需要两个位子的宽度,这时候我们计算存放地址和判断能否存下就要将个人的存储数据大小所算上,这样每个人的地址就可以由初始的存储地址加上之前人的存储大小就可以精确地算出来。所以我们可以随时算出地址进行数据的读取,时间复杂度为O(1)。我们通常把具有随时存取能力的存储结构称为随机存取结构。

3.3.2 对操作的解释

  1. 读取指定元素的下标:查找到该元素并返回下标减1(注意C和Python的数组第一个index都从0开始,所以开个玩笑有些程序员数数会从0开始数)
  2. 插入:将后面的数据后移一位
  3. 删除:将后面的数据前移一位
    所以我们可以知道插入和删除最少不移,最多移n—1位,所以平均时间复杂度为O(n—1/2),为O(n)。而读取则是O(1)。所以我们可以看出线性表顺序存储结构优缺点:

优点:
1、快速读取;
2、因为数据毫无联系所以无需为表述数据关系而增加额外的存储空间

缺点:
1、插入和删除需要移动大量的元素;
2、当线性表长度变化大时难以确定存储空间的容量(比如一会有人打电话告诉你我也要来图书馆自修,又有人有事不来,这样你就很难去找一个满意的空间);
3、造成存储空间的"碎片"(在数据存储领域中,碎片(fragmentation)是指存储空间使用效率低下,结果导致功能、运行效率变低或二者兼而有之的现象),个人理解的话:因为线性表的顺序存储必须要求数据个数小于数组个数,也就是所要存储的容量小于用于存储的容量。通俗了讲就是你的人数一定小于位子数。而又因为所要存储长度的不确定性会给存储总量带来较多的无利用到的存储容量,因而导致存储的效率变低,产生“碎片”。

3.4 线性表的链式存储结构
因为顺序存储的线性表在插入和删除数据所花的时间比较多,所以这便有了单链表结构。那么单链表结构又是什么?

因为我们在顺序存储结构的存储原理是相邻存储,因而会产生空余,所以链式结构打破了相邻存储,将不同的数据以一连串的链式结构存储下来,因而我们所有的存储结构是由若干个单链表所组成。所以这个就可以使得我们存储数据的时候可以跳跃地存储数据,也就是我们不必会因为下一个地址已经有数据而苦恼。那么怎么实现链表呢?我们这边就引入了指针,在存储当前的数据的时候,我们存储当前的数据和下一个数据的所指向的地址,因而我们在存储下一个数据的时候就可以去寻找前一个数据存储的指针所指向的地址,然后存储进去,再寻找空的地址指向下一个存储地址即可。所以这样的话我们就可以随机存储数据,也不用担心会有存储的‘“碎片”。

此外因为链式表之间结点之间的连接是由指针指向的,所以我们在插入数据或是删除数据的时候可以直接将我们所要操作数据的结点增加或者删除在分别将两个结点连接即可,在插入总我们将原来已有的两个结点之间的指向关系给去除,将前一个结点的指向指向插入我们数据的那一个结点,在将我们插入数据的结点指向原来的后一个结点。而删除的话就是将所包含数据的结点删去,再将前后的两个原来不连接的结点连接即可。

大一下开始学C语言了,但我总是感觉比其他学校的进度慢一些,老师讲的也不是特别快,到现在还没有讲到基本的链表的处理,这里先将有关代码的部分鸽一鸽 ————3.25
解释一下最近停更的原因,大学这一段时间的课程明显大一上的时候多出来很多课程,而且最近在进行mysql有关python爬虫的课程学习,我已经负债累累了,所以每天更一期的几乎不会实现了。最近我开始明白,有些时候文字可能还没有图片给大家的感受来的直观。在这片文章结束之后我会进行总的概括和进行故事性的串联,毕竟这样无论是对大家对深奥概念的理解还是代码之后的乐趣都是一件不错的事,那么请诸君做好,慢慢听我道来。

这边跳过双向链 pass 一段

4.栈和队列

  1. 概念
    举个简单的例子,我们往手枪之中塞子弹的时候,子弹是一个一个塞进去的,你只能对最上层的子弹做出取出和塞进的操作,你无法对已经塞进去的子弹进行任何的操作,除非将目前的子弹的上一个子弹进行取出。那么子弹也就是我们所说的数据,而塞进和取出的操作就是我们对数据进行的操作。

2、所以为什么会有一个栈的概念的提出

——————————未完待续——————————

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值