20220519数据结构绿皮书读书笔记

 

 

 

20220519数据结构绿皮书读书笔记

2.5.3 优化数据结构的定义

4个层次,1.概念,2.算法,3.编程,4.应用
分别对应抽象层 数据结构层 实现层 应用层
前两层聚焦于概念性的解决问题,中间两层更倾向于精确的方法来表达和操作数据,最后两层更关注如何写代码

编程原则
让你的数据结构化你的程序
一旦你的数据完全结构化了,你的算法就会几乎自然完成

总结

  1. 用数据结构来使你的程序逻辑变得清晰
  2. 实现数据结构的时候练习信息隐藏和封装,用方法来访问你的数据结构并保持他们在class内部,从应用程序程序中分离
  3. 尽可能推迟你数据结构实现的细节
  4. 栈是所有类型的数据结构中最简单的一个,当合适的时候使用栈
  5. 在任意问题中需要反转数据的时候考虑使用栈
  6. 避免使用奇技淫巧来存储你的数据,通常奇技淫巧不适用于其他场景
  7. 确认你的数据结构被初始化了
  8. 在算法设计的时候,永远细心小心的处理边界情况和极端情况,特别是数据结构是空或者是null的时候
  9. 在选择实现方案之前确认所有数据结构和操作在抽象层都完整定义了

3 队列

3.1 定义

队列是一种列表,所有添加都在一侧进行,所有的删除都在另一侧进行
添加的那侧叫做front或head,删除的那侧叫做rear或者tail,通常翻译为队首和队尾

3.1.1 操作

1.初始化
2.append元素,参数x,类型T,返回错误码
如果有空间,x将被添加到队尾,否则返回overflow
3.serve元素,无参,返回错误码
如果队列非空,队首第一个元素将被移除,否则返回underflow
4.取出元素,参数x,类型T,返回错误码
如果队列非空,队首的元素将会被记为x,否则返回underflow
6.判断是否为空,无参,返回布尔值
判断当前队列是否是空,如果是空返回true,否则返回false

3.1.2 扩展操作

可以通过继承扩展操作,比如增加full方法判断是否满了,增加serveAndRetrieve来同时获取和移除队首元素

is-a关系,扩展的队列和原来的队列政治为is-a关系,因为每一个被扩展队列的对象,都是一个队列。如果我们看到Every A is a B这种短语,我们应当考虑实现派生类A作为B的子类。注意,C++一般叫做派生类和基类,java叫做父类和子类,有的时候可能混用

考虑这么几个类,大学,学生,大学生,人。每个学生都是人,每个大学生都是学生,也是人。大学和大学生之间的关系就并不是is-a了,但每个大学都有大学生(现在可不一定,疫情可能封校了hhhh),他们之间的关系是has-a关系

3.2 队列的实现

1.物理模型
如果使用数组实现,其中一种策略是保证队首永远在数组的开始位置,然后每添加一个元素则增加计数器并放到数组最后一个元素的后面。移除操作则相反,然而为了移除第一个元素需要昂贵的代价,移除后需要移动后面每一个元素到前一个位置。当队列变长后这种操作会变得十分缓慢,虽然我们的存储模型非常接近实际的排队模型,但给计算机使用太不合适了,垃圾

2.线性实现
为了更有效率我们需要同时追踪队首和队尾的位置,而不移动实际的数据,当数据增加的时候和1一样,但当数据减少的时候,我们直接把队首记录往后挪一个即可。虽然这种方式避免了移动数据,但这种方式有一个坏处,队首和队尾永远会无限增长下去,前面的内存都被浪费了。虽然可以通过动态内存的方式释放前面浪费的内存,但这样下去很快队首和队尾的指针就会增长到极限,然后boom。垃圾

3.循环数组
逻辑上把数组连接成一个环形,数据当到达数组最后一个元素的时候,返回数组的第0个位置继续使用

4.实现循环数组
用小学二年级就学习过的余数就能解决,当超过数组大小后,继续对队首和队尾的位置取余数即可返回第一个位置
5.c++里的循环数组

6.边界条件
注意如何判断队列的空或满,当使用循环数组的时候,当队首和队尾位置一样的时候,极有可能是空,也有可能是满
不详细解释了,自己画图试试就懂了

7.解决方案
7.1.保留一个空的位置,不用了,浪费一个空间
7.2.记录一个flag来表示空或者满,或者记录size
7.3.记录一个特殊值,这个值永远不可能出现的值

8.总结
物理模型,模拟实际物理空间队列的情况
线性数组,对物理模型进行优化得到
循环数组,对线性数组进行优化得到

3.3 C/C++基于循环数组实现的队列

https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/3.1.queue.h
https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/3.1.queue.c

https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/3.2.queue.h
https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/3.2.queue.c

3.4 证明和测试

一个最简单的方式就是编写一个菜单驱动的测试程序,让我们选择所有可能的操作来观察执行结果

3.5.队列的应用:模拟

模拟飞机跑道,模拟飞机起飞等。。。通常计算机模拟可以用来代替昂贵的物理模型模拟,而提前观察或预测实际的情况

顺带一提B站之前有个16级的学弟xyx用计算机模拟疫情传播的视频,就是一个很好的计算机模拟的例子

总结

  1. 在选择实现方案之前确认所有数据结构和操作在抽象层都完整定义了
  2. 在选择实现方案时,需要考虑数据结构上必要的操作
  3. 如果每一个A类的实例都拥有所有B类实例的属性,考虑把A实现为B的派生类(这个不绝对,因为这个关系很可能被后期的需求变更打破)
  4. 当声明基类的时候,考虑派生类的需求
  5. 用public的继承可以实现is-a的关系
  6. 用成员变量的方式实现has-a的关系
  7. 可以用随机的变量来为随机事件建模

明天继续链表实现的栈和队列
mark 目前是113页 ,pdf是129页

 

0 Comments
Leave a Reply