数据结构——线性结构

终于可以开始填这个坑了233333
————————————
像上一篇文章说的数据结构要解决的问题是数据如何在内存中存储,以什么结构存储。
用张仰森老师的话来说,就是“数据以及数据之间的关系。”
今天主要要聊的就是线性结构。

这里说的线性结构,是指从逻辑结构上来划分的。
说到这,就不得不提下物理结构。所谓逻辑结构,就是在逻辑、理论上定义的结构,而物理结构是指在实际的内存中数据的存储结构。

比如,有这么一种结构,每个元素只有唯一的前驱、唯一的后继、第一个元素没有前驱、最后一个元素没有后继。满足这样要求的结构就是线性结构。
而上面所说的结构仅仅是逻辑上的结构,而不是物理上的结构。之前已经学过的数组、链表,都可以表示这种结构。但是数组在物理结构上来说每个元素就是相邻摆放的,而链表的节点在实际内存中的分布却是分散的,仅仅是由于指针的存在好像使得前后有了关系。

C语言已经内置支持了最简单的线性结构,就是数组。
声明一个数组之后,就能得到N个相邻的物理空间。这N个空间中的变量就满足之前说的规律。
而概念里说的元素,可以是一个int,也可以是一个结构体。在本系列文章或者代码、我通常会用一个整数来代表一个元素。
而实际的应用中,一个元素可能是一个结构体、指针(最常见的是结构体的指针)。

说完了数据和数据之间的个关系,那么这个数据结构就已经快说完了,还差对应的一系列操作。
这里仅列出简单的几个操作,也就是增删改查,实际实现时候包括但不限于以下几种,候根据需要来写。
向线性表插入一个元素
从线性表删除一个元素
从线性表中查找关键字为i的元素
修改线性表中某个元素

套路:确定操作之后如何开始写代码
0.定义自己的数据结构(这里以C为例,所以写的是结构体。不排除以后用Java类的可能性=-=)
然后对每个操作按下面四个步骤
1.确定函数名、参数类型、参数数量
2.预想函数执行前内存的状态
3.确定函数执行后预期的内存状态
4.返回值类型极其含义

接下来将以两种实现方式进行说明,参考代码在github(求star求follow):
第一种:用数组实现的线性表
头文件:https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/1.1.linearlist.h
源文件:https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/1.1.linearlist.c

0.定义一个新的类型,叫List
//我默认大家都能看懂C语言,看不懂的请回上一篇看基础测试或者重新看下C的教程=-=
如上面代码中5-9行所示,我把一个数组和数组的长度定义为一个List。
createList,创建一个列表,
参数:长度
执行前状态:无
执行后状态:初始化一个指定长度的List
返回值定义 返回一个List
其他的同理。。。。

定义完了函数先写测试程序,如上面的主函数。
然后依次实现每个函数,createList的时候注意,在申请完内存后一定要初始化内存中每个地址的默认值,要么用NULL要么用0要么用参数,不要啥都不做,免得给自己挖坑
其实这也就是构造函数做的事情。。。
(代码中createList的时候其实应该返回指针更好一些,不然会再复制一份函数内部的List再return回去,然而这是很久之前的代码了,我懒得重写了=-=凑合着看吧233333)
代码中的问题(现在看当时写的代码简直是naive=-=):
风格不是很好。。。比如我省略了一些大括号,这是不好的习惯。。。
容错处理并没有做到最好,有些地方并不是一定会成功的操作我都没有做判断,比如realloc之后的返回值可能为NULL然而我没判断。。。

第二种实现方法,用链表实现线性表:
参考代码我就不贴上来了,去github看吧。。。
地址:
头文件:https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/1.2.linearlist.h
源文件:https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/1.2.linearlist.c

这是最基本的线性结构,之后所有复杂的结构都是这两种结构的组合、延伸,说白了再复杂的结构最终也能用这两种形式来组合表达。
其实计算机内存是线性的,所以某种意义上来说,所有的存储都是在把非线性的结构转换成线性结构来表达、然后存储、运算。

建议自己亲手仿写一个自己版本的线性表。。。。
写完后再继续往下看。
刚刚的线性表我们可以对对任意位置、任意元素进行操作。
如果我们加一点点限制,就出现了新的数据结构(其实并不是新的数据结构,只是操作操作受限的线性表而已)
也就是栈、和队列。
如果我们限制只能在一端增加和删除元素,那么就是栈。
如果我们限制只能在一端增加、在另一端删除,那么就是队列。
代码稍稍改一改就能搞定,参考代码地址如下:
https://github.com/956237586/DataStructure-C/tree/master/DataStructure-C
其中2.1是用数组实现的栈。
2.2是用链表实现的栈、也叫链栈。
(待补充详细说明,链栈的部分有个经典写法。。。)
3.1是用数组实现的队列,一般来说为了节省空间,数组会重复利用已有的空余内存,所以一般是循环队列。
(待补充详细说明,循环队列的关键在push和pop如何复用空余空间。。。。)
3.2是用链表实现的队列。(未完待续)
——————————–
今天先写到这=-=之后再继续写。。。。

数据结构——基础测试

 

数据结构基础


前言

数据结构主要要解决的问题是:

  1. 数据如何在内存中存储,以什么结构存储。
  2. 对应不同的存储结构应该用什么样的方法对数据进行操作来满足相应的需求。

简单来说,上面第一点就是数据结构,第二点就是算法。不同的数据结构必然会有不同的算法来对数据进行操作。

学习需要的基础

  1. 任意一种编程语言基础。(比如C语言
  2. 熟悉理解内存的结构并能精确控制。(比如利用C的指针正确控制内存
  3. 面向对象的编程思想。

基础测试

阅读下面的代码,回答后面的问题,如果你都能和答案理解意思一样,说明:

  1. 你C语言基础已经可以学习数据结构并且不会有什么大问题。
  2. 你对内存结构有了深刻的理解。
  3. 你有了基本的面向对象思想,以及明白了面向对象在底层的部分实质。

代码如下:

问题:

  1. 第9行的目的是什么?
  2. newStudent的意义是什么,它的返回值类型为什么是Student?
  3. Student的本质是什么?
  4. 21行写成Student stu1;同时删掉22行行不行?
  5. 23行、28行为什么要用->,能不能用.(这是一个点)

答案解析将在之后更新。。。如果你无法回答,可以阅读思维导图指针教程部分的内容

答案

  1. 第9行的目的是把一组数据集合的指针定义为一种新的类型。
  2. newStudent的意义是创建新的学生,返回值是指向学生的指针。
  3. Student的本质是结构体的指针。
  4. 不行,仅有指针并没有申请内存,数据实际还不存在。
  5. 因为是结构体的指针,而不是结构体本身,所以用->访问。

内存分析

第15行
调用newStudent函数,传入参数。
申请1块内存stu1,把函数返回值赋值给stu1。

第20行:函数调用内部
申请一个结构体指针ret初始化为空。
用malloc动态申请一个结构体大小的内存,并把首地址赋给ret。
利用传入的参数初始化结构体的内容。
把ret里面的内容返回,ret在函数结束后被自动回收。

回到16行
把stu1的内容传入show函数。

第27行:函数调用内部
根据传入的stu访问里面的数据。

回到17行程序结束。

内存图解

15行函数调用前

mxdxjc1

函数调用过程,21行开始执行

mxdxjc2

21行执行完毕,开始执行22行

mxdxjc3

mxdxjc4

22行执行完毕,开始执行23行

mxdxjc5

23行执行完毕,返回15行,传回ret

mxdxjc6

开始执行16行

mxdxjc7

28-30行开始执行,根据this指针访问内存。函数结束,回收show函数空间。

mxdxjc8

 

总结

看到this是不是很熟悉??面向对象其实就是对一个数据集做一些操作,这些数据就叫属性,这些操作就叫方法。改改语法是不是和Java或者C++很相似??这些既是面向对象的底层部分细节,也是学习数据结构必要的知识,只有对内存了如指掌才有可能灵活的控制使用内存。