[C和指针] ch17. 经典抽象数据类型
第十七章:经典抽象数据类型
Github
链接:ch17. 经典抽象数据类型
抽象数据类型 (ADT)
是非常常用的,最为常见的就是数组、顺序表、链表、栈和队列等等。诸如 OS
内部的任务调度有队列、双向链表、红黑树等均被广泛应用。熟练掌握各种数据结构是非常重要且必要的。
本章总结及注意点
部分课后习题解答
17.9 问题
栈。
队列。
当然可以。程序员封装即可。
top()
取栈顶元素但不进行栈顶元素的出栈,pop()
函数进行栈顶元素的出栈。并不觉得有多强大。对于静态数组模拟的堆栈来讲,只需要将
top_element() = -1
这样进行赋值即能达到栈清空的效果。当然,C++ STL
容器中有单独的clear
函数用来清空容器。这完全取决于它的初始化是 -1 还是 0。
首先
assert()
主要在此用于栈判空、判满,其实也就是为了不让数组出现越界访问。若删除所有断言,则相当于数组可能会产生越界访问,这两者等价。链式堆栈中节点都是单独申请的,所以得单独释放。且
pop()
函数中已经实现了内存的释放,所以直接栈判空+pop()
即可。肯定不可以!这是个常见问题,当
malloc()
空间被free
后,对应指针不可再访问一个已经被释放的内存空间。所以拿个临时指针变量保存其指向的空间地址,最后释放这个指针变量指向的空间即可。答案给出了一个很不错的解释:
书中提到过。采用计数器确实比较简单,而空出一个数组元素不使用,则造成了空间浪费,当元素的大小较大时,这个空间浪费就越大。
这确实是一个比较麻烦的问题,总共会产生四种情况,队列为空、为满、
front
在前、在后。注意最后需要给算得的元素进行取模,当队列为空时,取模运算也会给出正确答案。见demo01.c
。队列又不需要反向遍历,双向链表在此并不适用,
STL
中deque
双端队列,就慢的鬼一样…BST
插入只会在其叶节点位置插入,不会修改原来树的结构,这点注意下就行了。和数组、链表相当,O(n)O(n)O(n)。
简单问题。
中序:左-根-右。后序:左-右-根。前序:根-左-右。其实能够发现:将前序遍历简单修改一下,变成:根-右-左,那么前序的逆序就是后序遍历。当然,我在此说的是迭代的写法,至于递归没啥好写的。可以见博文,四种遍历详细总结过:[M二叉树] lc145. 二叉树的后序遍历(栈+dfs)。
同上。简单看下参考答案吧:
中序遍历。四大遍历中没有可以直接得到降序序列的,但是简单修改中序遍历即可,另起变为 右-根-左,即可得到降序序列。
当然是后序遍历了,其在处理根节点之前,会将子节点处理完毕。
17.10 编程练习
这个函数必须为新的堆栈分配空间,并将旧堆栈的值复制到新的。然后它必须释放旧数组。断言用于确保新数组很大足够保存栈上当前的所有数据。见
demo02.c
。此模块被转换为
stack
模块。resize
函数更有趣:并不是数组中的每个位置都需要复制,而且当数据绕到数组的末尾时,front
和rear
很容易变得不正确。见demo03.c
。懒得写了,自行写完看看课后答案即可。就是简单的链表尾插和头删的基本操作。
这是简单的,堆栈数据被简单地声明为数组,作为参数传递的堆栈号选择要操作的元素。见
demo04.h、demo05.c
。简单的
dfs()
,统计二叉树的节点数量。常见的
bfs()
模板问题,在这是数组形式实现的BST
,也可参考我的博文 [M二叉树] lc102. 二叉树的层序遍历(队列+bfs)。没啥意思,中序序列是否为严格升序即可。拿递归直接判断也可以,力扣上这题肯定是有的,上去练手就行了,在这还得建树、写测试用例还测不全。
我的博文:[C++系列] 76. 详解BST二叉搜索树。数组形式实现大同小异。删除的四种情况需要额外注意,找不到该值,找到且为叶子节点,找到有单独的孩子节点,找到有两个孩子节点。还要找左子树中的最大值进行值替换,这个应该是前驱节点,最后删除这个前驱节点即可。
这最好使用后序遍历。不幸的是,遍历函数的接口设计传递一个指向节点的值,而不是一个指向该节点包含的值,所以我们必须编写自己的。
同 8。
书中采用一个宏使堆栈具备可声明多个、解决命名冲突、泛型等问题。但是还是瑕疵,例如同类型的函数命名将会一致等。在此将堆栈功能分解为三个宏,解耦合,很不错的想法。且在日常学习中针对宏写的还是太少了,尤其是利用宏来写函数功能,更是少之又少,见一个得积累一个。见
demo08.c
随笔
栈的三种实现方式。静态数组、动态数组、链式栈实现。一般在算法题中会选择静态数组的方式来进行模拟实现。动态数组实现涉及到内存分配函数的使用,以及一定要防止的内存泄露问题。链式栈的空间利用率相当高,但是涉及大量的改变指针指向的相关操作,我个人觉得效率上来讲肯定没有静态数组高!
队列的三种实现方式,和栈相同。但是由于静态数组实现队列的情况下会造成大量的空间浪费,所以采用循环数组的方式来进行优化,我们在此一般称其为循环队列。采用两个指针加一个空余数组元素进行判空、判满。其实采用一个计数变量也是相当香的。循环队列两个指针,其中
rear
指针初始化为 0,是为了在添加第一个元素的时候能和front
指针指向同一个位置。故每次push
元素的时候都是先将rear
指针先向后移动一位再对该位置进行赋值。判空判满的两个式子也是相当巧妙:(rear+1)%SIZE==front
即队列已空,这个可以从初始化中就可以看出来,一开始队列为空的时候front
是 1,rear
是 0。当rear == front
的时候,整个队列中就只有一个元素,再出队后front++
则在rear
的前面一个位置,所以上式判空成立。且由于rear
和front
中至少间隔一个空元素,那么当(read+2)%SIZE == front
的时候说明这个队列已经满了。动态队列书中未实现,链式队列比较简单。树,在此书中着重讲解了
BST
树,可参考我的博文,拿C++
实现的:[C++系列] 76. 详解BST二叉搜索树,同在该专栏下讲解了AVL
树和红黑树。链式BST
蛮不错的,消除了数组空间利用不充分的问题。其中,P377
链式二叉树的插入函数采用了两个指针,其中一个一级指针、一个二级指针,一级指针存储当前遍历到的树中的节点,二级指针指向当前节点的左右孩子指针指向的空间。二叉搜索树下所有的插入都只会在叶子节点中进行插入。实现的改进提出了
ADT
的三个问题:用户声明多个堆栈、支持泛型、解决命名冲突问题。在C++ STL
中,有了模板,这些问题自然迎刃而解。在C
语言中可以采用宏来解决这个问题。在ch14
中就已经提到过了:宏是类型无关的。并且实现了一个支持任意类型的malloc
函数。书中运用宏参数实现类型无关,将堆栈代码写成了一个宏,且添加了用户可以自定义的命名标识,用##
的方式加到函数名称的后面。用C
语言来实现泛型是相当困难的,然而面向对象的语言对泛型是具备良好的支持的。
疑问
链式实现栈、队列、
BST
等其实都比较生疏,以往确实没有写过。静态数组是写的最多的。关于利用宏来实现
C
语言下的泛型是值得考虑学习研究的事情!数据结构就得多刷题。
[C和指针] ch17. 经典抽象数据类型相关推荐
- C的指针疑惑:C和指针17(经典抽象数据类型)
堆栈这种数据最鲜明的特点是:后进先出. 用动态数组实现堆栈: #include "C17.h" #include <stdio.h> #include <stdl ...
- 【 C 】经典抽象数据类型(ADT)之内存分配
C中的一些抽象数据类型(ADT)如链表.堆栈.队列和树等,链表已经在前几篇博文有所讨论,见: [ C ]在单链表中插入一个新节点的尝试(一) [ C ]在单链表中插入一个新节点的尝试(二) [ C ] ...
- 《C和指针》笔记(十四)-- 经典抽象数据类型
C/C++ 笔记 QQ : 1841545843 邮箱 : jiaxx903@163.com 一. 堆栈 /* ** 堆栈模拟接口 */#define STACK_TYPE int// push vo ...
- 【 C 】经典抽象数据类型(ADT)之堆栈(用静态数组实现堆栈)
堆栈简介 堆栈(stack)最鲜明的特点就是后进先出(Last-In First-Out,LIFO)的数据进出方式. 基本的堆栈操作通常被称为 push 和 pop.push就是将一个新值压入到堆栈的 ...
- 数据结构第一次作业——抽象数据类型
1.作业内容(1分) ADT Rational{ 数据对象:D={e1,e2|e1,e2都是Elemtype类型} 数据关系:R={<e1,e2>} 基本操作:Builtration(&a ...
- 面向对象C语言编程--抽象数据类型-AbstractDataTypes
AbstractDataTypes C语言的灵活 C语言很灵活,不但有基础数据类型,char.int.double等,还允许程序员自定义类型,如: 定义一个链表使用的数据类型,其中有Node节点和自己 ...
- 实验报告:抽象数据类型的表现和实现
实验报告:抽象数据类型的表现和实现 实验内容 基本要求: 设计实现抽象数据类型"三元组",要求动态分配内存.每个三元组由任意三个实数的序列构成,基本操作包括:创建一个三元组,取三元 ...
- c语言 数据结构 list、queue、tree抽象数据类型的定义与实现 详尽代码和注释
本文使用c语言定义并实现list.queue.tree抽象数据类型,代码有详尽注释,可以通过代码熟悉原理并运用数据结构. 0.ADT基础知识 类型包括两类信息,属性和操作.在编程时,根据编程问题匹配合 ...
- 链表 队列 基本概念 为什么使用二叉查找树 抽象数据类型
文章目录 0.抽象数据类型(ADT)的优点? 1.为什么需要链表? 2.链表的概念? 3.队列的概念? 4.为什么需要二叉查找树? 0.抽象数据类型(ADT)的优点? ADT版本可读性高,隐藏编程细节 ...
最新文章
- k8s系列----一个简单的例子
- python-opencv3 kmeans图像分类
- python tkinter怎么读_Tkinter GUI与阅读系列
- tensorflow 小于_坐姿不对,屏幕就变模糊!教你用TensorFlow做一款“隐形背背佳”...
- 用python分析小说_用Python对哈利波特系列小说进行情感分析
- 希尔排序java写法_java高级排序之希尔排序
- Tensorflow Data Adapter Error: ValueError: Failed to find data adapter that can handle input
- Django 分页查询并返回jsons数据,中文乱码解决方法
- 1725.可以形成最大正方形的矩阵数目
- PHP高级——抽象类与接口的区别(转)
- 编程思想 —— 哨兵的使用
- MYSQL获取自增ID的四种方法
- HashMap,,ConcurrentHashMap------------------浅谈!!
- Windows网络编程之(二)Socket通信非阻塞模式Select(TCP和UDP)
- Java实现学生管理系统代码
- 《HBase权威指南》读书笔记6:第六章 可用客户端
- 网络安全——计算机网络拓扑图
- SpringBoot之下载Excel
- 读《人性的优点》有感
- 小菜鸟的自我激励与不服输的心