数据结构——线性结构(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] << " ";     }}

测试结果:
这里写图片描述

阅读全文
2 0
原创粉丝点击