数据结构——线性结构(1)——顺序栈的实现
来源:互联网 发布:索尼手机更新软件 编辑:程序博客网 时间:2024/06/05 15:21
我们前面介绍的Stack,Queue和Vector类是抽象数据类型的一般类别的例子,称为线性结构(linear structures),其中元素以线性顺序排列。这个系列讨论了这些类型的几种可能的表示方式,并考虑了表示形式如何影响效率。
因为线性结构中的元素以类似数组的顺序排列,使用数组来表示它们似乎是一个明显的选择。实际上,我们之前中提到的CharStack类是使用数组作为底层表示来实现的。然而,数组不是唯一的选择。栈,队列和vector也可以使用类似于我们上面所说中用于实现buffer的链接来实现。通过研究这些结构的链表实现,你将不仅增加对链表的理解,还有如何将它们应用于实际的编程环境中。
这里我们还有另一个目的。从之前我们可以看出,我们介绍的类与我们口中所说的CharStack类不同 - 因为它不限于单个数据类型。实际的Stack类允许客户端通过提供类型参数来指定值的类型,如Stack 或Stack 。然而,到目前为止,你只有机会使用参数化类型作为客户端,接下来你将学习如何实现它们。
Templates(模板)
在计算机科学中,能够对多种数据类型使用相同的代码称为多态(polymorphism)。 编程语言以多种方式实现多态。 C ++使用一种称为模板(Templates)的方法,其中程序员定义了可以用于许多不同类型的通用代码模式。 我们之前介绍的集合类(stack,vecto,queue)依赖于C ++模板工具,这意味着你需要了解模板的工作原理,才能了解集合类的基本实现。
在详细描述模板之前,我们最好回顾并重新审视重载(overload)的概念,这在函数中有介绍。重载允许你定义具有相同名称的多个函数,只不过这些函数可以通过其参数来区分即可。 给定一个特定的函数调用,编译器可以查看参数的数量和类型,并选择与该函数名相匹配的函数的版本。
这部分内容 详细参看:泛型编程——模板
用模板定义 栈(stack)
我们前面中的CharStack类定义了所有堆栈所需的相关操作,但是由于它只能存储char类型的元素,因此是有限的。为了获得库类的灵活性,有必要重新实现Stack作为一个模板类,它是一个使用C ++模板工具的类,以便可以使用任何数据类型。
将非模板类更改为模板类涉及一些简单的句法更改。 例如,如果要使用通用Stack模板替换我们所说的的CharStack类,则首先用Stack替换名称CharStack,然后在类定义之前添加以下行:
template <typename ValueType>
template关键字表示遵循此行的整个语法单元(在本例中为类定义),可用于ValueType参数的许多不同值的模板模式的一部分。在下面的类定义中,你可以随时使用占位符名称ValueType来指代要存储的元素的类型。 因此,当你将CharStack类定义转换为更一般的模板表单时,可以使用ValueType替换原型中的每个字符,用以pop,push和peek。 更新版本的stack.h界面如下图所示。 如你所看到的其他接口一样,该类的私有部分存储在一个单独的stackpriv.h文件中。
图下图中的stackpriv.h文件包含复制构造函数和赋值运算符的必要定义,以便可以将数据复制到堆栈中。 该版本的stackpriv.h文件将一个堆栈复制到另一个堆栈是非法的。 如我们之前所述,你还可以编写代码以制作堆栈的深层副本,以便复制堆栈不与原始堆栈共享数据。
头文件
/* *这部分文件实现我们之前所使用的stack类 *它主要的原理为 后进先出(LIFO) */ #ifndef _Stack_h #define _Stack_h /* *类型: Stack<ValueType> *此类建立一个称为堆栈的线性结构,其中仅从一端添加和删除值。 *这个规定产生了一个(LIFO)的行为,它是堆栈的定义特征。 *基本堆栈操作是push(添加元素到顶部)和pop(把元素从顶部删除)。 */ template <typename ValueType> class Stack{ public: /* *构造函数:Stack *用法:Stack <ValueType> stack *----------------------------- *初始化一个空栈 */ Stack(); //析构函数 ~Stack(); /* *方法:size() *用法:int n = stack.size(); *-------------------------- *返回栈中元素的个数 */ int size(); /* *方法:isEmpty() *用法:stack.isEmpty(); *-------------------------- *判断栈中元素是否为空 */ bool isEmpty(); /* *方法:clear() *用法:stack.clear(); *-------------------------- *清空栈中的所有元素 */ void clear(); /* *方法:push() *用法:stack.push(); *-------------------------- *向栈顶推入一个元素 */ void push(ValueType); /* *方法:pop() *用法:stack.pop(); *-------------------------- *移除栈顶的一个元素,并返回其值,如果栈空 则返回一个错误 */ ValueType pop(); /* *方法:peek() *用法:stack.peek(); *-------------------------- *返回栈顶的值,但是不移除,peek 偷看的意思,如果栈空 则返回一个错误 */ ValueType peek(); #include "stackpriv.h" //私有成员部分 }; #include "stackimpl.cpp" //将实现文件包含进来 #endif
私有部分
/* *这部分文件是基于数组实现的文件 *包含了stack的私有部分 */ private: const int INITIAL_CAPACITY = 10; //初始化刚刚开始的容量 /*实例化变量*/ ValueType* array; //指向动态数组的指针 int count; int capacity; /*私有方法声明*/ void expandCapacity(); /*禁止深层复制*/ Stack(const Stack & value) { } const Stack & operator=(const Stack & rhs) { return *this; }
实现部分
/* *这个文件用数组实现我们的栈,简称顺序栈 */ #ifdef _Stack_h #include "error.h" //构造函数 template <typename ValueType> Stack<ValueType>::Stack() { capacity = INITIAL_CAPACITY; array = new ValueType[capacity]; count = 0; } //析构函数 template <typename ValueType> Stack<ValueType>::~Stack() { delete []array; } //计算栈中的元素 template <typename ValueType> int Stack<ValueType>::size() { return count; } //计算栈是否为空 template <typename ValueType> bool Stack<ValueType>::isEmpty() { return count == 0; } //清空栈元素 template <typename ValueType> void Stack<ValueType>::clear() { count = 0; } //将一个元素压入栈中 template <typename ValueType> void Stack<ValueType>::push(ValueType ch){ if(count == capacity) expandCapacity(); array[count] = ch; count ++; //可以简写称为 array[count++] = ch; } //将元素弹出栈中 template <typename ValueType> ValueType Stack<ValueType>::pop() { if(isEmpty()) error("pop: Attempting to pop an empty stack"); return array[--count]; } //查看栈顶的元素 template <typename ValueType> ValueType Stack<ValueType>::peek() { if(isEmpty()) error("pop: Attempting to peek at an empty stack"); return array[count - 1]; } //拓展容量 template <typename ValueType> void Stack<ValueType>::expandCapacity() { ValueType *oldArray = array; capacity *= 2; array = new ValueType[capacity]; for (int i = 0; i < count; i++) { array[i] = oldArray[i]; } delete[] oldArray; } #endif
对于实现文件,我们有几点要注意:
1. 我们之前的CharStack的实现,主要是这样的形式
void CharStack::push(char ch) { if (count == capacity) expandCapacity(); elements[count++] = ch;}
而在这里我们是这样的:
template <typename ValueType>void Stack<ValueType>::push(ValueType value) { if (count == capacity) expandCapacity(); elements[count++] = value;}
其实我们就是多了一行template 。然后把char类型变为了ValueType类型。
2. 在使用模板类的时候,我们有下面的模板:
stackimpl.cpp文件,实现了类的方法,但在逻辑上是类定义的外部。 因此,实现的#include行出现在类定义本身的闭包括号之后。
3. 如果仔细查看stackimpl.cpp中的实现代码,你将看到它包含了与接口文件中看到的样式板相似的样板。 实现文件的全部内容都包含在以下预处理器行中:
这些行确保只有当该文件包含在定义_stack_h符号的stack.h接口中时才执行编译。 如果你尝试自行编译stackimpl.cpp文件,编译器将简单地忽略内容。一些编程环境会自动编译所有的.cpp文件,因此,在不需要时,确保不会编译此代码是很重要的。
3.关于array[count++]的一些讨论。我们可以写个小程序理解:
#include <iostream>using namespace std;void dispaly(int array[]);int main(){ int array[10]; for (int i = 0; i < 10; i++) { array[i] = i; } cout << "array is" << endl; dispaly(array); cout << endl; int n = 4; cout << "第五个数为" << endl; cout << array[4] << endl; array[n++] = 0; //将第五位赋值为0 cout << "将第五位赋值为0结果为" << endl; dispaly(array); cout << endl; cout<< "此刻的n的值为:" << endl; cout << n << endl; //但是此刻n的值为5 return 0;}void dispaly(int array[]){ for (int k = 0; k < 10; k++) { cout << array[k] << " "; }}
测试结果:
- 数据结构——线性结构(1)——顺序栈的实现
- 数据结构——线性结构(5)——顺序队列的实现
- 大话数据结构(一)——线性表顺序存储结构的java实现
- 数据结构——线性表的顺序存储结构
- 数据结构——线性表——顺序存储结构——C++实现线性表
- 数据结构——线性表(顺序结构和链式结构)
- 数据结构复习——线性表的顺序存储实现
- Java数据结构——线性表的顺序存储实现
- 数据结构线性表—静态顺序表的实现
- 数据结构—线性表的顺序表示与实现
- 数据结构——线性结构(4)——顺序队列与循环队列的原理
- 数据结构——栈的顺序结构以及实现
- 数据结构线性表顺序结构的实现
- 数据结构与算法——线性表顺序存储结构
- 数据结构之线性表—>顺序存储结构
- 线性表——顺序结构(c语言实现)
- 线性表——顺序结构(c语言实现)
- 数据结构—线性结构—线性及其实现
- VS2013+opencv3.3的安装(或自行编译源码)及配置
- hdu 6180 Schedule(贪心)
- kmalloc和vmalloc的区别和联系
- 2017/8/25
- [OpenGL] 网格细分算法 Loop Subdivision
- 数据结构——线性结构(1)——顺序栈的实现
- [翻译]扫描线算法(Line Sweep Algorithm)(2)
- 训练总结8.25
- 数学杂题 鸡兔同笼
- 创建视图
- virt-install命令参数
- HDU6180 Schedule
- 编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad
- win7下启动activeMQ服务