1.为ADT分配内存有三种技巧:静态数组、动态分配的数组和动态分配的链式结构。静态数组对结构施加预先确定固定长度的这个限制。动态数组的长度可以再运行时计算,如果需要数组也可以进行重新分配。链式结构对值的最大数量并未加以任何限制。
2.堆栈。后进先出。
堆栈提供三种接口:push,pop,top
top返回顶部元素的值,但它并不把顶部元素从堆栈中移除。
3.堆栈需要另外另个额外的函数:
需要一个函数告诉我们堆栈是否为空
如果堆栈存在最大长度限制,需要一个函数告诉我们堆栈是否已满。
4.在数组堆栈中,所有不属于外部接口的内容都被声明为static,这可以防止用户预定义接口之外的任何方式访问堆栈中的值。
变量top_element保存堆栈顶部元素的下标值。它的初始值为-1,提示堆栈为空。
5.动态数组实现堆栈。
要增加两个函数。
create_stack传经堆栈。参数指定堆栈可以保存多少个元素。
destroy_stack销毁堆栈,用于释放堆栈所使用的内存。避免内存泄露,这个函数式必须的。并且将对战的 长度改为0.
6.链式堆栈
不再需create_stack函数,但是可以实现destroy_stack用于清空堆栈
由于链式堆栈不会满,所以is_full函数始终返回假。
7队列
另外一种接口是,delete函数从队列的头部删除一个元素,但并不返回它,first函数返回队列第一个元素的值但并不将它从队列中删除。
链式和动态分配实现的队列需要使用的create_queue和destroy_queue函数的原型。
8.队列的实现需要两个指针。一个指向对头,一个指向队尾。
从队尾插入,从队头删除。
好的方案是让队列环绕到数组的头部,这样新元素就可以存储到以前删除元素所留出的空间中,这个方法常常被称为循环数组。
9
rear+=1;
if(rear>QUEUE_SIZE)
rear=0;
和 rear=(rear+1)%QUEUE_SIZE;效果一样。QUEUE是数组的大小,rear是最大指针,所以比QUEUE小1。例如a【5】其实最大指向是a【4】
10判断循环数组是否满和空。
重新定义满的含义。保留一个元素始终不用。
当队列满时,对头和队尾相差2.以前是相差1
当队列空时。当队列只有一个元素时,对头和队尾都指向这一个元素。为了能都指向这个元素,再插入后增加rear的值,当第一次插入后指向这个插入的元素。则队列为空时
rear的值必须比front小1.删除最后一个元素的情况也是如此。
这样空出一个元素,当队列为空的时候rear和front相差1.
删除最后一个元素后的状态是。也是相差1.
所以满足下列条件队列为空(rear+1)%QUEUE_SIZE==ront
满足下列条件队列为满:(rear+2)%QUEUE_SIZE==ront
11.链式队列,测试队列是否为空只是简单测试链表是否为空就可以了。
12树
二叉搜索树具有一个额外的属性,每个节点的值比它的左子树的所有节点的值都要大,但比它的右子树的所有节点的值都要小。
13.在二叉搜索树中插入,采用递归算法
14在二叉搜索树中删除节点。
如果删除的节点有两个孩子,则先删除它左子树中最大的那个节点,然后用这个值来替换原来的那个节点。
15.pre_order_traveres 它的参数是一个回调函数指针,它所指向的函数在树中处理每个节点被调用,节点的值作为参数传递给这个函数。
do_pre_order_rraveres
16. 链式搜索树。
find函数只用于验证树是否存在于这个树中。
把值插入到一个有序单链表有相同的技巧。
17.destory_tree用于四方所有分配给这棵树的内存。
18.用#define 宏实现代码,然后用目标类型进行扩展。或者通过把需要存储到ADT的值强制转换为void*。这种策略的缺点是它绕过了类型检查。