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

本文介绍了C++中的Stack类实现,包括类定义、方法实现、封装原则,并通过计算器和括号匹配应用展示数据结构的使用。讨论了代码风格问题和可能的编译器行为差异。重点关注了抽象数据类型(ADT)的概念,特别是栈作为ADT的应用实例。
摘要由CSDN通过智能技术生成

个人博客
https://blog.hylstudio.cn/archives/945

为了良好的阅读体验,建议到个人博客或CSDN,QQ空间就是备份用的,tx看起来彻底放弃日志了。。。。

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

2.2.2 类的定义

const int maxstack = 10; // small value for testing
class Stack {
public:
    Stack();
    bool empty() const;
    Error_code pop();
    Error_code top(Stack_entry &item) const;
    Error_code push(const Stack_entry &item);
private:
    int count;
    Stack_entry entry[maxstack];
};

2.2.3方法实现

具体实现C语言版参考我大学写过的代码

https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/2.1.stack.h

https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/2.1.stack.c

https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/2.2.stack.h

https://github.com/956237586/DataStructure-C/blob/master/DataStructure-C/2.2.stack.c

2.2.4 封装

注意我们的实现强制客户端来使信息隐藏,声明的private可见性的数据不可能被正式暴露的几个方法以外的方式来修改。这种数据可见性的好处是,无论客户端如何操作,正常来说不可能出现非法数据。

编程原则
数据结构的公开方法应当被实现成没有任何前提条件。成员数据应当保持私有化

2.3 应用:计算器

逆波兰式计算器
比较简单,都应该会写,略过

case '-': numbers.push(numbers.pop() − numbers.pop());

假设这个代码工作正常,解释下为什么这种风格是不好的?
显然同一行最好只做同一个动作,多个动作同时在一行代码里,一旦出错将难以定位是哪个方法的错误
有没有可能两个不同的C++编译器,都是严格遵循C++标准,但执行这个语句执行出不一样的结果?
理论上可能,numbers.pop() − numbers.pop()这个表达式的执行可能不是严格的先后顺序,导致两个数位置完全相反?

2.4 应用:括号匹配

相对简单,代码判断条件略复杂一点
在线处理算法需要stack记录所有的左括号,当碰到右括号需要和栈顶对比
非在线处理算法可以偷懒,读取过程可以先算下总数是否匹配,如果不匹配直接结束
匹配的话,再确定嵌套层次是否正确

2.5 抽象数据类型以及实现

2.5.1 介绍

在数据的逻辑结构和物理结构之间划分好边界可以更好的帮助我们设计程序。
举个例子:树形结构的逻辑结构是严格区分父子节点的,而树形结构的物理结构并不是。
我们可以使用三元组、链表、二维数组等多种方式表达相同的一个树形结构,但这些结构并不影响我们解决最终的问题
有时根据需求不同还需要对同一个逻辑结构同时使用多个物理结构来加速计算。举个常见的例子,部门的树形结构往往同时需要知道根节点到当前节点的路径,也需要按层次快速查找,还经常需要知道当前部门是第几层。如果按教科书教的树形结构存储,会发现大量数据都得重新计算,而在实践中,如果树形结构不经常变动,往往会将这些额外的信息提前计算后存到内存或者数据库,来方便后续开发

首先第一步是认识数据之间逻辑上的连接 和 在逻辑结构上表达这些连接

2.5.2 通用概念

1.数学定义

关键定义:“类型"是一个集合,集合的元素叫做这个类型的"值”
我们常说的integer类型意味着所有的整数集合,real类型意味着所有的实数集合,character类型意味着所有的字符的集合
注意,这里已经可以明确的区分抽象数据类型和实现了:例如C++里的int类型,其实并不是所有的整数集合,集合的大小取决于计算机的CPU字长。同理float和double也通常意味着浮点数类型(分别存储了尾数也就是指数、指数也就是幂次,详见计算机组成原理),他们是所有实数中很小的一部分

2.原子性和结构化/结构体类型

如int,float,char通常成为原子类型,因为我们认为他们的值仅有一个元素,没有任何可分割的意图。计算机语言C++提供了如数组、类、指针等工具可以让我们构建一个新的类型,称之为结构体类型。单独一个值被封装进结构化类型是一个结构化的对象。一个结构化类型的价值有两个方面:1.它可以包含多个组件。2.它是有结构的,是按一组规则把组件放到了一起的
一般来说就像我们在数学中用应用的工具:集合、序列、函数一样。我们学习list的时候,list是有穷序列,但是当我们用数学定义的时候,可能是无穷的。

关键定义,有穷序列(finite sequence)定义如下:
A sequence of length 0 is empty. A sequence of length n ≥ 1 of elements from a set T is an ordered pair (Sn−1, t) where Sn−1 is a sequence of length n − 1 of elements from T , and t is an element of T .
自己细品定义,不翻译了
国内的翻译五花八门,其实看英文原文的话,有的细节就是形容词和名词的区别

从这个定义中我们可以清楚的划分sequential和contiguous的定义区别,sequential表示这些元素能形成sequence。contiguous意味着这些元素在内存地址上是相邻的。
sequential描述的是逻辑结构,而contiguous描述的是物理结构
因此我们可以这么说a sequential list in a contiguous implementation,也就是顺序表的连续实现(之所以这么翻译是因为还有顺序表的离散实现,也就是链表)

3. 抽象数据类型

有限序列(finite sequence)的定义让我们可以尝试开始定义list了:a list of items of a type T is simply a finite sequence of elements of the set T.
关键定义:T类型的集合中元素的有穷序列称之为T类型的list

细心的你一定发现了,stack的定义和list的定义没什么区别,唯一的区别限定是在对元素的操作上

一个T类型元素的有穷序列结合下列操作,称之为T类型元素的栈

  1. "创建"栈
  2. 测试栈是否为"空"
  3. 给定一个"不满"的栈, "Push"一个新的元素到栈顶
  4. 给定一个非空的栈,从栈顶"Pop"一个元素
  5. 给定一个非空的栈,获取栈"顶"一个元素的值

注意这个定义没说怎么实现,称之为ADT,也就是抽象数据类型:
ADT有两个部分:1.定义数据和数据之间的关系。2.定义可以在这些数据上进行什么动作

明天继续
mark 目前是74页2.5.3 ,pdf是91页

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值