数据结构——线性结构(1)——顺序栈的实现

我们前面介绍的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] << " "; 
    }
}

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

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值